Как аргумент для обратного вызова в React передается от дочернего компонента к родительскому компоненту? - PullRequest
0 голосов
/ 24 ноября 2018

Я работал над учебником по React на https://reactjs.org/tutorial/tutorial.html, и я справляюсь с этим достаточно хорошо.У меня есть одна вещь, которую я так и не смог обернуть вокруг.Вот полный код (codepen: https://codepen.io/gaearon/pen/EmmOqJ?editors=0010):

function Square(props) {  
    return (
      <button className="square"
              onClick={props.onClick}>

          {props.value}
      </button>
    );
}

function calculateWinner(squares) {
    const lines = [  
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6],
    ];

    for (let i = 0; i < lines.length; i++) {
        const [a, b, c] = lines[i];

        if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
            return squares[a];
        }
    }

    return null;
}

class Board extends React.Component {
    renderSquare(i) {
        return (
            <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)}/>  
        );
    }

    render() {  
        return (
            <div>
                <div className="status">{status}</div>
                <div className="board-row">
                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}
                </div>
                <div className="board-row">
                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}
                </div>
                <div className="board-row">
                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}
                </div>
            </div>
        );
    }
}

class Game extends React.Component {
    constructor(props) {
        super(props);  

        this.state = {
            history: [{
                squares: Array(9).fill(null),
            }],
            xIsNext: true,
        };
    }

    handleClick(i) {
        const history = this.state.history;
        const current = history[history.length - 1];
        const squares = current.squares.slice();  

        if (calculateWinner(squares) || squares[i]) {  
            return;
        }

        squares[i] = this.state.xIsNext ? 'X' : 'O';

        this.setState({
            history: history.concat([{
                squares: squares,
            }]),  
            xIsNext: !this.state.xIsNext,
        });  
    }

    render() {
        const history = this.state.history;
        const current = history[history.length - 1];
        const winner = calculateWinner(current.squares);

        let status;

        if (winner) {
            status = 'Winner: ' + winner;
        } else {
            status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
        }

        return (
            <div className="game">
                <div className="game-board">
                    <Board  
                        squares={current.squares}
                        onClick={(i) => this.handleClick(i)}  
                    />
                </div>
                <div className="game-info">
                    <div>{status}</div>
                    <ol>{/* TODO */}</ol>
                </div>
            </div>
        );
    }
}

// ========================================

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

. В учебнике они передают функцию обратного вызова handleClick(i) из родительского компонента Game дочернему компоненту Board и оттуда кдочерний компонент Square. Мой вопрос о том, как установить аргумент i? Я предполагаю, что отправная точка - это когда renderSquare(i) вызывается в компоненте Board. Оттуда я теряюсь относительно того, как i он добирается до handleClick(i). Он хранится в 'onClick function object passed to Square from Board`?

Ответы [ 3 ]

0 голосов
/ 24 ноября 2018

Ваши догадки были хорошими!Я попытался прокомментировать ключевые аспекты кода в этой сущности: https://gist.github.com/Sangrene/d6784a855e0e3324e5b499e3a5242c45

0 голосов
/ 24 ноября 2018

Вы правы в понимании потока и происхождения функции onClick, это определено в компоненте игры более высокого уровня.

Компонент Game определяет метод onClick, который может быть вызван для обработки чего-либо.

В обратном порядке,

  1. Компонент HTML [button] дает обещание сказать, что если вы дадите мне функцию обратного вызова, которая соответствует указанной сигнатуре, ябудет вызывать его всякий раз, когда что-то происходит ( нажатие в этом событии ).

  2. Теперь более высокий компонент (родительский для кнопки) функции кнопки Square, обазаинтересованы в

    • с использованием кнопки и
    • заинтересованы в получении информации о том, когда происходит событие, согласно кнопке обещания, предложенной в пункте (1).

Но поскольку сама функция Square не имеет реального варианта использования этого события, она решила делегировать обработку этого события своему родителю так же, как и кнопка.

Но это еще один шаг, чтобы сказать, что если вы заинтересованы в получении этого уведомления, мой контракт будет расширен, и я сообщу о событии только в том случае, если вы передадите свой обратный вызов черезчлен моего входного объекта как onClick.

Таким образом, другой компонент более высокого уровня Board (родительский для Square) должен каким-то образом удовлетворять требованиям компонента Square передачи объекта с членом onClick.Обратите внимание на некоторые вещи, однако, метод renderSquare члена Board,

  • Получает в качестве входного аргумента 'i', который он использует для оценки чего-то (...), устанавливающего значение и onClick свойства при запуске нового экземпляра Square и последующем возврате этого экземпляра (поскольку JS поддерживает функции высшего порядка функции высшего порядка )
  • Он определяет anonymous function какделегат, реализация которого он просто должен вернуть члену реквизита с именем onClick (. Похоже, что у Правления также был контракт с любым потенциальным родителем, что должен быть член onClick, который был передан ему.) ... обратите внимание, что поскольку этот синтаксис еще не принят официально, babel используется для преобразования этого кода в то, что понимает обычный браузер.

  • renderSquare и сам Board не делает ничего для передаваемого члена onClick, кроме как просто возвращает его в своей собственной анонимной функции на onClick={() => this.props.onClick(i)}

Полагаю, вы знаете, как будет выглядеть результат этого утверждения, но ради аргумента оно в основном становится function onClick(){return [this].props.onClick(i);}

Корневой компонент Game (Board parent) - это тот, который определяет и обеспечивает фактическую реализацию, которая удовлетворяет требованиям метода onClick (, определенного компонентом кнопки ), ипередает его как props дочернему компоненту Board.Теперь это означает, что всякий раз, когда Board сталкивается с onClick, в частности, из его props, он, по сути, будет обращаться к анонимной функции, определенной в Game, которая впоследствии возвращает Game handleClick функцию, см. Пункт 3.2.выше.

Чтобы попытаться ответить на ваш вопрос

У меня вопрос о том, как я задаю аргумент?Я предполагаю, что отправной точкой является вызов метода renderSquare (i) в компоненте Board.Оттуда я заблудился относительно того, как он делает свой путь, чтобы обработать Click (i).Хранится ли он в объекте функции onClick, переданном в квадрат от Board?

Самая важная вещь, на которую следует обратить внимание, это то, что весь этот код выше - это весь план или определения того, как код будет работать, когда он активен или вызывается, представьте, что это просто конвейер.Таким образом, определение не означает, что handleClick(i) вызывается сразу, на самом деле, он не вызывается до тех пор, пока наш компонент кнопки не инициирует событие, когда что-то заставляет кнопку / удовлетворено опубликовать событие, из которых, когда это происходит,обратное объяснение этих шагов, которые я только что пытался объяснить, происходит до тех пор, пока не будет запущена функция handleClick для корневой игры.

Я понимаю, что мой ответ может быть очень длинным, но я надеюсь, что он раскрасит сообщение и, надеюсь, поможет вам визуализировать поток вещей.

0 голосов
/ 24 ноября 2018

Я предполагаю, что отправной точкой является вызов метода renderSquare (i) в компоненте Board.Оттуда я теряюсь в том, как я могу справиться. Нажмите (i)

Вы на правильном пути.

В пределах Board.render(), this.renderSquare(...) называетсяс номером 1~8.

И renderSquare имеет обработчик onClick, this.props.onClick(i).

renderSquare(i) {
    return (
        <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)}/>  
    );
}

this.props передается от родителя, Game.render().
Итак this.props.onClick от <Board onClick={...} />

class Game extends React.Component {
    handleClick(i) {
       ...
    }

    render() {
        ...
        return (
            <div className="game">
                <div className="game-board">
                    <Board  
                        squares={current.squares}
                        onClick={(i) => this.handleClick(i)}  
                    />
                </div>
                ...
            </div>
        );
    }
}

Так this.props.onClick соответствует <Board onClick={...} />
, где он реализован как (i) => this.handleClick(i), в котором this.handleClick(i) относится к Game.handleClick(i).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...