React 实现井字棋游戏 (tic-tac-toe) 教程 (5) <译自官方文档>

2023-11-10

React 实现井字棋游戏 (tic-tac-toe) 教程 (1) <译自官方文档>
React 实现井字棋游戏 (tic-tac-toe) 教程 (2) <译自官方文档>
React 实现井字棋游戏 (tic-tac-toe) 教程 (3) <译自官方文档>
React 实现井字棋游戏 (tic-tac-toe) 教程 (4) <译自官方文档>

4-存储历史步骤

我们来实现这样的功能:通过重新访问 board 旧的状态,穿越回到之前的某一步。目前我们已经做到:每走一步棋,都随即创造一个新的squares数组。由此,我们可以同步地存储 board 的旧状态。

我们准备在状态中存储这么一个对象:

code

history = [
  {
    squares: [
      null, null, null,
      null, null, null,
      null, null, null,
    ]
  },
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, null,
    ]
  },
  // ...
]

我们希望由顶层的 Game 组件来负责显示一个列表,以展示每一步棋的历史。所以,就像之前我们把 Square 中的状态提升到 Board 组件一样,现在我们进一步把状态从 Board 提升到 Game 组件。这样,在顶层就有了我们需要的全部信息。

首先,Game 组件中添加一个constructor,设置初始状态:

code

class Game extends React.Component {
  constructor() {
    super();
    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 组件,让它通过 props 接收squares,同时由 Game 组件来规定其onClick属性,就像之前我们对 Square 组件做的一样。你可以把每个小方格的位置传进点击事件处理器里,这样我们仍然能知道被点击的小方块是哪一个。你需要完成这些步骤:

  • 删除 Board 组件中的constructor
  • 在 Board 组件的renderSquare中,把this.state.squares[i]替换为this.props.squares[i]
  • 在 Board 组件的renderSquare中,把this.handleClick(i)替换为this.props.onClick(i)

现在,整个 Board 组件看起来是这样:

code

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

Game 组件的render应该显示历史步骤记录,并接管游戏状态(status)的计算:

code

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

Game 组件现在渲染了 status,所以我们可以从 Board 组件render函数中删去<div className="status">{status}</div>,以及计算status的相关代码:

code

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

下一步,我们需要把 Board 组件中handleClick方法的实现移动到 Game 组件。你可以从前者中剪切下来,粘贴到后者。

我们还需要进行一点点改动,因为 Game 组件的状态和前者的相比,构成略有不同。Game 组件的handleClick能通过连接 (concat) 新的历史入口 (history entry),向栈中添加 (push) 新的 entry。

Game 组件的handleClick方法通过.concat()把新的步骤记录加入到数据栈中,由此构成新的新的储存历史步骤的数组。

code

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

现在,Board 组件仅仅有renderSquarerender就可以了;状态初始化和点击事件处理器就都放到 Game 组件去了。

查看最新的代码

显示每一步棋

我们把游戏进行到现在所走的每一步棋都展示出来。我们已经知道,React 元素是 JS 的“头等对象”,可以被储存、传送。为了渲染多个 React 的多个条目,我们传入了一个包含 React 元素的数组。构建它最常用的方法就是,对你的数组使用.map。在 Game 组件的render方法中,咱们就这么干:

code

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

    const moves = history.map((step, move) => {
      const desc = move ?
        'Move #' + move :
        'Game start';
      return (
        <li>
          <a href="#" onClick={() => this.jumpTo(move)}>{desc}</a>
        </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>
    );
  }

查看最新的代码

对于历史记录里的每一个步骤,我们都建立一个列表条目<li>,里面有一个<a>标签,它不指向任何地址(href="#"),而是会带有一个点击事件处理器,我们很快就会实现它。写代码至此,你应该会得到一个列表,记录着游戏中的历史步骤,还有一行警告:

Warning: Each child in an array or iterator should have a unique “key” prop. Check the render method of “Game”.

下篇,我们来谈谈这条警告是什么意思。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React 实现井字棋游戏 (tic-tac-toe) 教程 (5) <译自官方文档> 的相关文章

  • 【Ubuntu+python2】编译并运行PyQt5程序

    文章目录 前言 一 环境搭建 1 下载sip和PyQt5 2 移除本机自带sip 二 解压编译 1 sip解压编译 2 PyQt5解压编译 make j4编译过程出现报错error waitForEvents is not a member
  • Ionic3开发教程 - 开发(2)

    Ionic3开发系列教程Ionic3开发教程 环境准备 1 Ionic3开发教程 开发 2 Ionic3开发教程 发布Android版本 3 Ionic3开发教程 发布IOS版本 4 Ionic3开发教程 更新 5 本文中介绍的Ionic3
  • Ubuntu14.04终端配置:颜色、大小写不敏感、上键搜索字符串开头的历史命令、右键显示打开终端_ubuntu一站式配置教程(三)

    Ubuntu14 04终端配置 颜色 大小写不敏感 上键搜索字符串开头的历史命令 右键显示打开终端 ubuntu一站式配置教程 三 16单独列出来 文章目录 Ubuntu14 04终端配置 颜色 大小写不敏感 上键搜索字符串开头的历史命令
  • React路由懒加载的实现

    React lazy 通过引入lazy Suspense两个方法实现路由懒加载 首先 我们需要在组件中引入lazy Suspense这两个方法 然后我们需要通过Suspense组件 包裹着注册路由 import React Componen
  • 关于Vue.js和React.js,听听国外的开发者怎么说?

    VueJS 与 ReactJS 到底怎么样如何 听听别人怎么说 使用所有新的库和框架 很难跟上所有这些库和框架 也就是说 这就需要您决定哪些是值得花时间的 让我们看看人们说什么 和Vue JS一起工作是很愉快的 我发现学习曲线很浅 然而 这
  • React之state、hooks性能分析

    目录 一 state 1 为什么使用setState 2 setState异步更新 3 如何获取异步的结果 4 setState一定是异步吗 5 源码分析 6 数据的合并 7 多个state的合并 二 为什么需要Hook 三 Class组件
  • 内存文件系统提升磁盘性能瓶颈

    author skate time 2011 08 22 提升磁盘性能瓶颈 linux的内存文件系统 ramdisk ramfs tmpfs ramdisk 是块设备 在使用它们之前必须用选择文件系统将其格式化 并且调整文件系统大小比较麻烦
  • 2023最新ChatGPT网站源码+支持GPT4+Ai绘画+用户会员套餐+邀请分佣功能+支持后台一键更新+永久更新!

    2023最新ChatGPT网站源码 支持GPT4 Ai绘画 用户会员套餐 邀请分佣功能 支持后台一键更新 永久更新 可同时 单独 开启或者关闭GPT3 5和GPT4 0两种ChatGPT提问模型 用户可切换 次数套餐也是分开的 支持手机电脑
  • react 上传文件(多选)功能入的坑

    1 这里报错是因为onChange的this指向不对 解决方法在constructor中写 this onChange this onChange bind this 或者在绑定事件的时候写 onChange this onChange b
  • React官方文档--Lifting State Up

    Lifting State Up 如果 几个组件需要同时影响并且修改同一个数据 我们建议将这个共享状态进行提升 给他们最近的共同祖先 在这个部分 我们将要创建一个温度计算器来判断水会不会在给定温度下沸腾 我们将从一个叫做BoilingVer
  • umi 后台管理demo

    umi 后台管理demo umi react ts dva antd egg 有待优化 简单的前后端管理demo 接口提供增删查改 前端也有相应功能 github代码 https github com huiBuiling ql admin
  • 如何在 Vultr 上部署 ONLYOFFICE 文档 v7.3

    现在您可使用通过 Vultr 市场提供的一键式应用在 Vultr 架构中轻松部署 Docker 版本的 ONLYOFFICE 文档 一键式应用是什么 一键式应用是一个包含所有必要预配置组件的镜像 可用于便捷地在运行有 Ubuntu OS 的
  • hooks实践总结

    何为hooks 在React中hook是指不编写 class 的情况下使用 state 以及其他的 React 特性 而Vue3也推出了具有相同功能的组合式API 如果你用过Vue3就会知道在 setup 中你应该避免使用 this 因为h
  • 【排错】error: error parsing recommended.yaml: error converting YAML to JSON: yaml: line 14:的解决方式

    在部署k8s的时候 编写k8s的dashboard文件 遇到以下错误 error error parsing recommended yaml error converting YAML to JSON yaml line 14 could
  • vue发展历史简介

    基本介绍 Vue 是一套用于构建用户界面的 渐进式框架 与其它大型框架不同的是 Vue 被设计为可以自底向上逐层应用 最初它不过是个人项目 时至今日 已成为全世界三大前端框架之一 github 上拥有 17 8万 Star 领先于 Reac
  • 如何替换对象的key值

    发生的场景 现在用antd组件库 有些组件想渲染数据的话 我要根据他们官网给的字段名称对应起来才能渲染上去 这个是复选框选中 保存的时候 字段需要按照后台约定的传入code value 1 常规循环遍历 大招来了 哈哈哈 才疏学浅 我觉得是
  • 自定义hooks

    自定义传参hooks 把大多数的通用代码 或者props 抽成一个hooks 用hooks实现上下文 就不用再一一传参了 实现原理 主要是通过createContext useContext 生产 消费者模式 用于大量props一层一层传参
  • Typora+Picgo(正常) 却上传图片失败问题解决思路和办法

    报错信息 在typora中粘贴图片时报错 显示上传图片失败 有点奇怪 而我确定我的picgo正常且通过了测试 那我们就去看日志 跟踪排查问题在哪里 我的picgo日志文件路径在 D user username Application Dat
  • React Jsx转换成真实DOM过程?

    面试官 说说React Jsx转换成真实DOM过程 一 是什么 react 通过将组件编写的 JSX 映射到屏幕 以及组件中的状态发生了变化之后 React 会将这些 变化 更新到屏幕上 在前面文章了解中 JSX 通过 babel 最终转化
  • Git Bash教程

    Git Bash教程 Pull操作 Pull操作 输入 git pull 呈现 base root xx git pull https github com xx xx git 得到 remote Enumerating objects 5

随机推荐

  • 【win10】电脑剪贴板失效,解决办法。

    1 打开任务管理器 把剪贴板的进程结束 2 打开运行 输入rdpclip exe 即可解决
  • 状态压缩DP

    状态压缩DP前置知识 问题简介 基于状态压缩的动态规划 又叫集合动态规划 顾名思义 这是一类以集合信息为状态的特殊的动态规划问题 主要有传统集合动态规划和基于连通性状态压缩的动态规划两种 一般的动态规划往往着眼于整体 从中提取出两三个关键信
  • docker 安装mongodb

    1 取最新版的 MongoDB 镜像 gt docker pull mongo latest 2 查看本地镜像 gt docker images REPOSITORY TAG IMAGE ID CREATED SIZE mongo late
  • 数据库查询优化

    文章目录 1 代码优化 2 定位到慢SQL上 并优化 3 合理使用索引 重点 4 分表查询 5 缓存 6 异步 多线程 1 代码优化 减少没有必要的代码 例如for循环次数过多 作了很多无谓的条件判断 相同逻辑重复多次等 2 定位到慢SQL
  • 微服务分布式构架开发实战 附下载地址

    微服务是一种软件架构风格 目标是将一个复杂的应用拆分成多个服务模块 每个模块专注单一业务功能对外提供服务 并可以独立编译及部署 同时各模块间互相通信彼此协作 组合为整体对外提供完整服务 以往的图书大多只针对微服务分布式架构自身的知识点讲解
  • Linux iptables常用命令

    iptables 是 Linux 中重要的访问控制手段 是俗称的 Linux 防火墙系统的重要组成部分 这里记录了iptables 防火墙规则的一些常用的操作指令 下面的操作以 CentOS 为基础介绍 应该对不同的 Linux 发行版都差
  • 日志分析系列之平台实现

    本系列故事纯属虚构 如有雷同实属巧合 平台实现前的说明 小B在给老板汇报了 统一日志分析平台 项目后 老板拍板立即开始做 争取下一次能及时发现攻击并且追踪攻击者 于是小B开始分析了市面上商业与开源的日志分析平台架构 大家都神似如下图 知道了
  • 基于知识图谱的个性化学习推荐系统的设计与实现_kaic

    摘 要 Abstract 1 绪 论 1 1 研究背景及意义 1 2 国内外现状研究 1 3 研究工作和论文结构 2 相关技术 2 1 HTML 语言 2 2 Python 语言 2 3 数据库技术 2 4 Django 框架 3 系统分析
  • Nvidia Jetson 编解码开发(7)Jetpack 4.x版本Multimedia API 硬件编码开发--输出端对接ROS publish

    1 前言 Nvidia Jetson 编解码开发 6 Jetpack 4 x版本Multimedia API 硬件编码开发 输入端对接Camera V4L2采集 free xx的博客 CSDN博客 基于上篇基于开发 需求 1 2路Camer
  • C语言void指针及使用注意事项详解

    void 指针是一种特殊的指针 表示为 无类型指针 在 ANSI C 中使用它来代替 char 作为通用指针的类型 由于 void 指针没有特定的类型 因此它可以指向任何类型的数据 也就是说 任何类型的指针都可以直接赋值给 void 指针
  • js如何实现一个文本框只能输入数字 且是100的倍数

    var a 123 b 200 d test a a 100 0 false d test b b 100 0 true 转载
  • 安全网络通信(SSL&JSSE)

    目录 一 概念介绍 1 SSL简介 2 加密通信 3 安全证书 4 SSL握手 二 keytool工具生成证书 三 JSSE简介 1 KeyStore KeyManager与TrustManager类 2 SSLContext类 3 SSL
  • Linux系统更换默认启动内核版本方法

    1 得到当前系统已安装的所有内核版本 root localhost grep menuentry boot grub2 grub cfg cut d f2 CentOS 3 10 0 el7 x86 64 24 Workstation Ed
  • Python 微积分数值和符号计算(计算机代数)

    在积分学中 定积分是一个运算符 给定实值变量的实值函数和区间 a b 关联到该函数是其图形在区间 a b 中所包含的区域 给定一个变量的函数的以下积分 1 5 2 x
  • 购买阿里云服务器,先试试主机免费试用能抢到不 ...

    阿里云提供6个月的免费试用期 购买阿里云服务器之前先试试能不能免费抢到使用主机 先试用再正式购买 用起来更放心哈 步骤 1 进入阿里云导航栏的最新活动页 选择新手专区里面的阿里云免费套餐 2 根据自己账户的实名认证信息是个人认证还是企业认证
  • 12-24小时制

    编写一个程序 要求用户输入24小时制的时间 然后显示12小时制的时间 输入格式 输入在一行中给出带有中间的 符号 半角的冒号 的24小时制的时间 如12 34表示12点34分 当小时或分钟数小于10时 均没有前导的零 如5 6表示5点零6分
  • 自动控制原理知识点梳理——整体框架

    用的是胡寿松自动控制原理第七版 内容跟着书本和课上PPT 章节设置差不多 整体思路如下图 第一章 自动控制的一般概念 第二章 控制系统的数学模型 第三到五章分别是时域分析法 复频域分析法 根轨迹 频域分析法 第六章线性系统的校正方法 第八章
  • 各种坐标系下的散度、梯度、旋度公式

    引言 本文介绍了散度 梯度和旋度在直角坐标系 柱坐标系和球坐标系三种常见坐标系下的表示 记录一下 具体可以利用梅拉系数进行推导 谨记 梯度 标量求梯度得到矢量 散度 矢量求散度得到标量 旋度 矢量求旋度得到矢量 1 直角坐标系 标量表示 f
  • 使用CDN服务时遇到【HTTP PUT PATCH DELETE等请求方法不支持】【请求未到源站】【CDN直接返回404】【Cloudreve无法删除文件】的问题及解决方案

    异想之旅 本人原创博客完全手敲 绝对非搬运 全网不可能有重复 本人无团队 仅为技术爱好者进行分享 所有内容不牵扯广告 本人所有文章仅在CSDN 掘金和个人博客 一定是异想之旅域名 发布 除此之外全部是盗文 给赶时间的朋友们一句话总结 阿里
  • React 实现井字棋游戏 (tic-tac-toe) 教程 (5) <译自官方文档>

    React 实现井字棋游戏 tic tac toe 教程 1 lt 译自官方文档 gt React 实现井字棋游戏 tic tac toe 教程 2 lt 译自官方文档 gt React 实现井字棋游戏 tic tac toe 教程 3 l