react三子棋后续

前言:延接上文继续,将最后的game组件讲完

我在之前那篇文章中说过game的作用是用来存储board的每一个历史状态的,那么具体放在游戏中可以用来做什么,保存下每一个历史状态,其实意味着悔棋(哈哈哈)在上一篇文章中,为了保留每一个square的状态,我们用到了状态提升,将每一个square的状态交给了他的管理者(老大)board管理,这里其实也是同样的道理,我们将board的状态及交互交给game组件来实现

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(9).fill(null),
      }],
      xIsNext: true,
    };
  }

history数组存储board状态,和上一篇文章中讲到的很像,此外,board除了交出自己的状态以外,它拥有的管理权也应该全部上交(点击事件)

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

以上代码是实现的board将click事件与状态进行提升以后的处理方法,board 的render方法也不应该再去判断输赢条件了应该交给game组件去判断:

render() {
    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>
    );
  }
 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,
    });
  }

第一段代码是交权,第二段代码是收到权力

我们已经将状态的存储这一问题解决掉了,下面我们要处理的是我们是怎么通过一个按钮来实现history的来会转换呢

 const moves = history.map((step, move) => {
      const desc = move ?
        'Go to move #' + move :
        'Go to game start';
      return (
        <li>
          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      );
    });

我们通过上面的函数来渲染button以及悔棋(map有点疑问),但是jumpto函数还没有写,jumpto函数顾名思义,是当点击desc的时候回到对应的状态并且创建一个新按钮,虽然我们将各个状态都可以放在history数组中,但是怎么才能确定你回到的状态和你想要的状态是一致的呢,这里我们想一下上一篇文章中的判断输赢的方法,每一个square都有一个对应的数组下标我们通过那个下标与原来就已经放好的winnner数组中的每一个小数组去配对,在这里,我们实际上可以认为那个下标是用来标识我们每一个square状态的一个key(键)也就是说,我们是通过key来找到的状态,这里也是同样的道理,每一个历史状态也需要找到一个key形成一个map(映射)这样我们就能够准确的找到,在这里我们选move来当作每一个历史状态的key,move表示的是已经下过的步数,这样我们记下这个步数然后去从history中取出一个相应步数的片段

handleClick(i) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    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
        }
      ]),
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext
    });
  }

加入跳转到某一个状态后继续下棋,那么状态的走向将会沿着现在的状态走下去,之前这个状态往后的状态将会被删掉

这里的stepNumber就是上面提到的move

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

    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>
      );
    });

最后jumpto函数:

jumpTo(step) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    });
  }

重新设置stepnumber与xisNext的值然后交给渲染函数去处理界面

小结:本文又强调了状态提升的用法,其中有一些细节性的东西需要注意:

1当在一个原来已经是一个状态提升的组件中继续状态提升的时候,需要将这个组件的状态以及管理权(事件处理)全部删除以及修改

2当涉及到处理某个动态数据时,最好给每一个动态数据都加上一个key便于查找


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值