前言
上一篇已经完成了圈圈叉叉的游戏,现在要加入"回到上一步"的功能。上一篇的程式中使用了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学习指南