前端框架 使用React 开发一个井字棋(4) React 保存历史记录

本文介绍如何在React应用中实现井字棋游戏的保存历史记录功能,包括创建不可变的squares副本,将历史记录保存在history数组中,提升状态到Game组件,展示历史步骤记录,并实现通过点击按钮选择历史步骤的功能。
摘要由CSDN通过智能技术生成

接下来是最后一个练习,我们将实现“回到过去”的功能,从而在游戏里跳回到历史步骤。

保存历史记录

如果我们直接修改了 square 数组,实现时间旅行就会变得很棘手了。

不过,我们可以使用 slice() 函数为每一步创建 squares 数组的副本,同时把这个数组当作不可变对象。这样我们就可以把所有 squares 数组的历史版本都保存下来了,然后可以在历史的步骤中随意跳转。

我们把历史的 squares 数组保存在另一个名为 history 的数组中。history 数组保存了从第一步到最后一步的所有的棋盘状态。history 数组的结构如下所示:

history = [
  // 第一步之前
  {
   
    squares: [
      null, null, null,
      null, null, null,
      null, null, null,
    ]
  },
  // 第一步之后
  {
   
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, null,
    ]
  },
  // 第二步之后
  {
   
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, 'O',
    ]
  },
  // ...
]
再次提升状态

我们希望顶层 Game 组件展示出一个历史步骤的列表。这个功能需要访问 history 的数据,因此我们把 history 这个 state 放在顶层 Game 组件中。

我们把 history state 放在了 Game 组件中,这样就可以从它的子组件 Board 里面删除掉 square 中的 state。正如我们把 Square 组件的状态提升到 Board 组件一样,现在我们来把 state 从 Board 组件提升到顶层的 Game 组件里。这样,Game 组件就拥有了对 Board 组件数据的完全控制权,除此之外,还可以让 Game 组件控制 Board 组件,并根据 history 渲染历史步骤。

首先,我们在 Game 组件的构造函数中初始化 state:

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

  render() {
   
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{
   /* status */}</div>
          <ol>{
   /* TODO */}</ol>
        </div>
      </div>
    );
  }
}

下一步,我们让 Board 组件从 Game 组件中接收 squares 和 onClick 这两个 props。因为当前在 Board 组件中已经有一个对 Square 点击事件的监听函数了,所以我们需要把每一个 Square 的对应位置传递给 onClick 监听函数,这样监听函数就知道具体哪一个 Square 被点击了。以下是修改后的结果:

class Board extends React.Component {
   
  handleClick(i) {
   
    const squares = this.state.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
   
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
   
      squares: squares,
      xIsNext: !this.state.xIsNext,
    });
  }

  renderSquare(i) {
   
    return (
      <Square
        value={
   this.props.squares[i]}
        onClick={
   () => this.props.onClick(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(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值