React 学习笔记_15(React 圈圈叉叉练习 - 2)

前言

上一篇已经完成了圈圈叉叉的游戏,现在要加入"回到上一步"的功能。上一篇的程式中使用了slice()这个方法来複製squares,其目的就是在于可以记录每一次squares的变化,不会直接改变到原始的资料,为了达到"回到上一步"这个功能,需要将上一步的squares Array储存起来。

Step 1 : 再次提升State

我们希望最顶层的Component(Game)能够纪录所有squares的变化,所以把State提升到最顶层。

Game :
1.提升State到本层,并建立History来存放所有squares的变化。

class Game extends React.Component{    //Step 1:提升State到本层    constructor(props)    {        super(props)        this.state = {            history: [{                squares: Array(9).fill(""),              }],              xIsNext: true        }    }    render()    {        return(            <div className="game">            <div className="game-board">                <Board />            </div>        </div>        )    }}

Board :
1.移除本层的state。
2.移除本层的handleClick funciton。
3.将this.state更改为this.props(本层state改为父层传递)。

class Board extends React.Component{    //Step 1:移除本层的state。        //Step 2:移除本层的handleClick funciton。        renderSquare(i) {        //Step 3:将this.state更改为this.props        return <Square value={this.props.squares[i]} onClick={() => this.props.handleClick(i)}/>;    }    render()    {        const Winner = calculateWinner(this.state.squares);        let status;        if(Winner)        {            status = "Winner :" + Winner;        }        else        {            status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');        }        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>                <dic>                    <button onClick={this.Reset}>ReStart</button>                </dic>            </div>        )    }}

Step 2 : 在Game中新增上一步的动作并显示游戏状态

Game :
1.建立上一步动作。
2.显示游戏状态。

class Game extends React.Component{    constructor(props)    {        super(props)        this.state = {            history: [{                squares: Array(9).fill(""),              }],              xIsNext: true        }    }    render()    {        const history = this.state.history;        const current = history[history.length - 1]; //Step 1:建立上一步动作        const winner = calculateWinner(current.squares); //Step 2:显示游戏状态        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 />            </div>        </div>        )    }}

Board : 在Game中已经显示了游戏的状态,便可以将本层的显示游戏的状态功能移除
1.移除显示游戏的状态功能。

class Board extends React.Component{    renderSquare(i) {        return <Square value={this.props.squares[i]} onClick={() => this.props.handleClick(i)}/>;    }    render()    {        //Step 1:移除显示游戏的状态功能                return(            <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>        )    }}

Step 3 : 移动handleClick function

Game :
1.将handleClick function从 Board component 移到 Game component
2.将squares与handleClick function透过props传递给子层

class Game extends React.Component{    constructor(props)    {        super(props)        this.state = {            history: [{                squares: Array(9).fill(""),              }],              xIsNext: true        }    }    //Step 1:新增handleClick function    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">                //Step 2:将squares与handleClick function透过props传递给子层                <Board                 squares={current.squares}                onClick={(i) => {this.handleClick(i)}}                />            </div>        </div>        )    }}

Step 4 : 展示过去的动作

Game :
1.新增一个function来判断是否有下一步动作(以改变button的文字)并选染到一个button中。
2.新增按钮被点击后所触发的funciton(JumpTo)。
3.修改render中选取最后一个动作,根据stepNumber来选取动作。

class Game extends React.Component{    constructor(props)    {        super(props)        this.state = {            history: [{                squares: Array(9).fill(""),              }],              //Step 2-1:在State中新增一个stepNumber来计算步数。              stepNumber: 0,              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,        });    };    //Step 2:新增onclick function    jumpTo = (move) => {        this.setState({            stepNumber : move, //Step 2-2:更新stepNumber状态            //Step 2-3:检测目前步数是否为偶数(判断下一步为"X"(偶数)还是"O"(基数))            xIsNext : (move % 2) === 0         });    };    render()    {        const history = this.state.history;        //Step 3:根据stepNumber来选取动作        const current = history[this.state.stepNumber];         const winner = calculateWinner(current.squares);         //Step 1:判断是否有下一步,并将结果选染到button中        const moves = history.map((step, move) => {            const desc = move ?              'Go to move #' + move :              'Go to game start';            return (              <li key={move}>                <button onClick={() => this.jumpTo(move)}>{desc}</button>              </li>            );          });        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>{moves}</ol>            </div>        </div>        )    }}

参考资料 :
React学习指南


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章