React中的回调参数如何从子组件传递到父组件?

时间:2018-11-24 02:54:16

标签: javascript reactjs callback

我一直在通过https://reactjs.org/tutorial/tutorial.html上的React教程进行学习,并且我对它的理解也足够好。我有一件事,但是我无法把头缠住。这是完整的代码(代码笔: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广场from委员会中吗?

3 个答案:

答案 0 :(得分:3)

  

我的猜测是,起点是在Board组件中调用renderSquare(i)时。从那里,我迷失了它如何处理ClickClick(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)

答案 1 :(得分:2)

您的猜测很好!我试图根据以下要点评论代码的关键方面:https://gist.github.com/Sangrene/d6784a855e0e3324e5b499e3a5242c45

答案 2 :(得分:2)

您对onClick函数的流程和起源的理解是正确的,它是在更高级别的组件Game中定义的。

Game组件定义方法onClick,可以调用该方法来处理某些事情。

相反,

  1. HTML组件[button]可以说,如果您给我一个 与所述签名匹配的回调函数,我将其称为 每当发生任何事情(在此事件中点击 )。

  2. 将高级组件(按钮的父级)添加到按钮Square 函数,都对

    感兴趣
    • 使用按钮和
    • 有兴趣按照诺言按钮通知事件何时发生 在第(1)点中提供。

但是因为Square函数本身没有     该事件的实际用例,它决定委托处理     将该事件传递给它的父按钮,就像按钮一样。

但进一步说,如果您有兴趣接收此通知,则我的合同将进一步扩展为:仅当您通过以下方式传递回调时,我才允许通知该事件:我的输入对象为onClick

  1. 因此,另一个更高级别的组件Board(Square的父级)将 某种程度上需要满足传入对象的组件Square的要求 与成员onClick在一起。不过请注意,renderSquare的{​​{1}}方法成员

    • 在启动Board的新实例并随后返回该实例时,将其用于评估(...)设置值和onClick属性的输入参数'i'(因为JS支持高阶函数Higher Order Functions
    • 它将Square定义为委托,其实现只是返回一个名为anonymous function的道具成员(董事会似乎也有一个与任何潜在的父代签订合同,必须有一个成员onClick传递给它。)...请注意,由于尚未正式采用此语法,因此使用babel将这些代码转换为典型浏览器中的代码会明白。

    • onClick方法和/或Board本身不对已传递的onClick成员执行任何操作,只是在renderSquare

    我假设您知道该语句的结果如何,但是为了论证,它基本上变成了 onClick={() => this.props.onClick(i)}

    1. 根组件function onClick(){return [this].props.onClick(i);}(父Game)是定义并提供满足Board(由按钮组件定义的)要求的实际实现的组件。 )方法,并将其作为onClick传递给子组件props。现在,这意味着无论何时Board特别是从Board遇到onClick时,它实际上都将访问props中定义的匿名函数,该匿名函数随后返回{{1} }的Game函数请参见上面的第3.2点。

尝试回答您的问题

  

我的问题是我如何设置论点?我的猜测是   起点是在开发板中调用renderSquare(i)时   零件。从那里我迷失了自己如何走向   handleClick(i)。是否存储在传递的'onClick函数对象中   toSquarefromBoard`?​​

这里要注意的最重要的一点是,上面所有这些代码都是有关活动或调用时代码如何工作的所有蓝图或定义,将其视为一条管道。因此,该定义并不意味着Game会立即被调用,实际上直到稍后我们的按钮组件触发事件,某些原因导致/满意的按钮发布事件(其中的某件事)时才调用它碰巧,这些步骤的相反过程一直一直发生,直到调用根Game的handleClick函数为止。

我意识到我的回答可能会很长,但是我希望它能描绘出一条信息,并希望可以帮助您可视化事物的流程。