React--井字棋小游戏

2023-11-02

安装较新版本的node.js,这里使用的是v15.0.1,用以记录React学习笔记。

1 搭建本地开发环境

在想要创建项目的文件夹下输入cmd
在这里插入图片描述
在这里插入图片描述
回车
在这里插入图片描述
输入命令npx create-react-app my-app等待项目初始化
在这里插入图片描述
等待一段时间,等待时间长短跟网络有关。
出现以下页面本地环境搭建就成功了。
在这里插入图片描述
这只是本地的git仓库,但是在开发中肯定是要团队合作的,所以这里我关联一下远程仓库
首先在GitHub上新建一个仓库
然后拷贝仓库地址 在这里插入图片描述
在新搭建好的本地环境文件夹中右键打开git bash
在这里插入图片描述
执行git remote add origin 你的仓库地址
再执行git push -u origin master将本地仓库推送到GitHub仓库的origin/master分支
在这里插入图片描述
在GitHub master分支上就可以看到本地代码了。
在这里插入图片描述

2 游戏开发

2.1 初始化

删除src目录里的所有文件,
新建index.css index.js
拷贝初始化代码
index.css

index.css
body {
    font: 14px "Century Gothic", Futura, sans-serif;
    margin: 20px;
  }
  
  ol, ul {
    padding-left: 30px;
  }
  
  .board-row:after {
    clear: both;
    content: "";
    display: table;
  }
  
  .status {
    margin-bottom: 10px;
  }
  
  .square {
    background: #fff;
    border: 1px solid #999;
    float: left;
    font-size: 24px;
    font-weight: bold;
    line-height: 34px;
    height: 34px;
    margin-right: -1px;
    margin-top: -1px;
    padding: 0;
    text-align: center;
    width: 34px;
  }
  
  .square:focus {
    outline: none;
  }
  
  .kbd-navigation .square:focus {
    background: #ddd;
  }
  
  .game {
    display: flex;
    flex-direction: row;
  }
  
  .game-info {
    margin-left: 20px;
  }

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Square extends React.Component {
    render() {
      return (
        <button className="square">
          {/* TODO */}
        </button>
      );
    }
  }
  
  class Board extends React.Component {
    renderSquare(i) {
      return <Square />;
    }
  
    render() {
      const status = 'Next player: X';
  
      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>
      );
    }
  }
  
  class Game extends React.Component {
    render() {
      return (
        <div className="game">
          <div className="game-board">
            <Board />
          </div>
          <div className="game-info">
            <div>{/* status */}</div>
            <ol>{/* TODO */}</ol>
          </div>
        </div>
      );
    }
  }
  
  // ========================================
  
  ReactDOM.render(
    <Game />,
    document.getElementById('root')
  );

这里使用的vscode,快捷键ctrl + ~打开终端,执行npm start
在这里插入图片描述
在这里插入图片描述

2.2 使用props传递数据

在Board中的renderSquare方法中给Square组件添加属性value={i}

renderSquare(i) {
      return <Square value={i}/>;
    }

Square组件中render方法button值改为{this.props.value}

render() {
      return (
        <button className="square">
          {this.props.value}
        </button>
      );
    }

效果为:
在这里插入图片描述

2.3 state实现交互功能

Square组件添加构造方法

    constructor(props) {
        super(props);
        this.state = {
            value: null,
        };
    }

render方法中onClick设置state

render() {
      return (
        <button className="square" onClick={() => {
            this.setState({
                value: 'X'
            })
        }}>
          {this.state.value}
        </button>
      );
    }

效果:点击之后出现’X’
在这里插入图片描述

2.3 状态提升

当你遇到需要同时获取多个子组件数据,或者两个组件之间需要相互通讯的情况时,需要把子组件的 state 数据提升至其共同的父组件当中保存。之后父组件可以通过 props 将状态数据传递到子组件当中。这样应用当中所有组件的状态数据就能够更方便地同步共享了。
将Square中初始化提升到Board中
Board中写构造方法constructor,并初始化

    constructor(props) {
        super(props);
        this.state = {
            squares: Array(9).fill(null),
        };
    }

组件Square中,render方法

    render() {
        return (
            <button className="square"
                onClick={() => this.props.onClick()}
            >
                {this.props.value}
            </button>
        );
    }

组件BoardrenderSquare

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

handleClick方法.slice() 方法创建了数组的一个副本,而不是直接修改现有的数组:

    handleClick(i) {
        const squares = this.state.squares.slice();
        squares[i] = 'X';
        this.setState({
            squares: squares,
        });
    }

这里Square组件只有一个render函数,那么完全可以将其重写为一个函数组件(如果你想写的组件只包含一个 render 方法,并且不包含 state,那么使用函数组件就会更简单。我们不需要定义一个继承于 React.Component 的类,我们可以定义一个函数,这个函数接收 props 作为参数,然后返回需要渲染的元素。函数组件写起来并不像 class 组件那么繁琐,很多组件都可以使用函数组件来写)

function Square(props) {
    return (
        <button className="square"
            onClick={() => props.onClick()}
        >
            {props.value}
        </button>
    )
}

2.4 轮流落子

设置’X’为首先落子方,state中定义一个布尔变量,每次点击设置该变量反转

    constructor(props) {
        super(props);
        this.state = {
            squares: Array(9).fill(null),
            iXNext: true,
        };
    }

点击

    handleClick(i) {
        const squares = this.state.squares.slice();
        squares[i] = this.state.iXNext ? 'X' : 'O';
        this.setState({
            squares: squares,
            iXNext: !this.state.iXNext, //反转
        });
    }

2.5 决出胜者

定义胜利时棋盘可能存在的情况
判断三个棋子连成线即为胜利

function calculateWinner(squares) {
    const lines = [
        [0, 1, 2],
        [0, 4, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 4, 6],
        [2, 5, 8],
        [3, 4, 5],
        [6, 7, 8]
    ];
    for (let i = 0; i < lines.length; i++) {
        const [a, b, c] = lines[i];
        if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
            return squares[a];
        }
    }
    return null;
}

Board组件的render中调用该方法

render() {
        const winner = calculateWinner(this.state.squares);
        let status;
        if (winner) {
            status = "Winner: " + winner;
        } else {
            status = 'Next player: ' + (this.state.iXNext ? 'X' : 'O');
        }
        // const status = 'Next player: ' + (this.state.iXNext ? '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>
        );
    }

最后修改handleClick函数,如果有胜利者或者玩家点击已经有值的square,则直接return

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

效果图:
在这里插入图片描述

3 时间旅行

3.1 保存历史记录

在以上代码中,使用slice方法保存了数组的副本,这是实现保存历史记录的关键所在。
这里需要再次将代码进行提升,把初始化,点击监听事件,部分render()提升至Game

    constructor(props) {
        super(props);
        this.state = {
            history: [
                {
                    squares: Array(9).fill(null),
                }
            ],
            iXNext: true,
        }
    }
    handleClick(i) {
        const history = this.state.history;
        const current = history[history.length - 1];
        const squares = current.squares.slice();
        // const squares = this.state.squares.slice();
        if (calculateWinner(squares) || squares[i]) {
            return;
        }
        squares[i] = this.state.iXNext ? 'X' : 'O';
        this.setState({
            /**
             * concat方法不会改变原数组, 与push不一样。
             */
            history: history.concat([{
                squares: squares,
            }]),            
            iXNext: !this.state.iXNext,
        });
    }
    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.iXNext ? '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>
        );
 	}

Board组件中删除构造方法和handleClick方法

renderSquare(i) {
        return (
            <Square value={this.props.squares[i]}
                onClick={() => this.props.onClick(i)} />
        )
    }
    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>
        );
    }

历史记录就已经保存下来了。

3.2 实现时间旅行

使用数组的map方法,把历史步骤映射为代表按钮的 React 元素,然后可以展示出一个按钮的列表,点击这些按钮,可以“跳转”到对应的历史步骤

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

constructor中添加stepNumber

    constructor(props) {
        super(props);
        this.state = {
            history: [
                {
                    squares: Array(9).fill(null),
                }
            ],
            iXNext: true,
            stepNumber: 0
        }
    }

实现jumpTo方法

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

效果:
在这里插入图片描述

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

React--井字棋小游戏 的相关文章

  • json字符串,本地存储讲解localstorage 和 sessionstorage及cookie,模板字符串初识

    这里写目录标题 json字符串 json格式的使用方法 对象的深拷贝狭义实现 localstorage 和 sessionstorage的区别 cookie 封装cookie函数 模板字符串初识 json字符串 abc123truelkgs
  • JS修改对象属性的值

    当对象以数字为属性名时 其实就是一个数组 用 var foo 1 a 2 b foo 1 c console log foo 1 c 2 b 当对象以字符为属性名时 直接用 获取属性值 var bar a 1 b 2 c 3 bar a h
  • react 上传文件(多选)功能入的坑

    1 这里报错是因为onChange的this指向不对 解决方法在constructor中写 this onChange this onChange bind this 或者在绑定事件的时候写 onChange this onChange b
  • windows下配置Mysql-5.7.9服务

    第一步 从官方网站下载 mysql 5 7 9 winx64 zip 第二步 解压缩 在根目录下复制my default ini 改名为my ini 第三步 初始化mysql目录 bin mysqld initialize user mys
  • React核心概念:状态提升

    上一节 表单 下一节 组合vs继承 React核心概念 状态提升 引言 添加第二个输入框 编写转换函数 状态提升 经验总结 引言 很多情况下我们使用的多个组件需要对同一个数据做出对应的反应 在这里我们推荐把这个共享的状态提升到距离这些组件最
  • React官方文档--Lifting State Up

    Lifting State Up 如果 几个组件需要同时影响并且修改同一个数据 我们建议将这个共享状态进行提升 给他们最近的共同祖先 在这个部分 我们将要创建一个温度计算器来判断水会不会在给定温度下沸腾 我们将从一个叫做BoilingVer
  • React class组件、react-hook函数组件分别实现五子棋

    react class类组件 react hook函数组件分别实现五子棋 前言 使用create react app脚手架简单搭建 不用安装其他依赖 纯 js css实现 这里就只是简单的说明目录结构和思路 具体的代码实现请转到 Githu
  • javascript,声明变量和导入时,大括号的特殊用法

    作为一个新手 今天看到一段奇怪的代码 定义变量时用大括号把变量名括起来了 还有import时也使用了大括号 import getToken from utils auth let data request 一脸懵 这是啥意思 度娘一番 记录
  • js 对数组对象进行排序

    let listData id 1 name 测试1 presenttime 1557883600000 id 2 name 测试2 presenttime 1580751813000 id 3 presenttime 1561448381
  • 6.js--布尔值和null

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 目录 1 布尔数据 boolean 2 null 3 null和undefined的意义 1 布尔数据 boolean var a true var b false
  • 节流与防抖

    1 我们先了解为什么要节流和防抖 我们给一个inpu输入框绑定一个oninput事件 此时我们输入 前端开发 四个字 我们 观察以下后台打印
  • JavaScript string中includes、startsWith和endsWith的使用

    文章目录 前言 一 includes 二 startsWith 三 endsWith 总结 前言 JavaScript string的这三个方法都是根据参数返回true或false 一 includes includes 方法判断一个字符串
  • uni-app项目中使用scss语法

    最近正在学习uni app开发 我先把文档浅略翻了遍 发现组件和接口几乎都是按照微信小程序走 但是视图层上的语法又是按照vue的语法走的 所以开发过程一定要注意这点 然后我想在uni app项目中使用scss语法 但是具体怎么安装呢 历经曲
  • js实现鼠标悬停显示title效果

  • node中间件是什么意思?

    node中间件是什么意思 2020 09 11 16 11 17分类 常见问题 Node js答疑阅读 1757 评论 0 中间件是一种独立的系统软件或服务程序 分布式应用软件借助这种软件在不同的技术之间共享资源 中间件位于客户机 服务器的
  • 探索CSS:从入门到精通Web开发(二)

    前言 当我们谈论网页设计和开发时 CSS 层叠样式表 无疑是其中的重要一环 作为HTML的伴侣 CSS赋予网页以丰富的样式和布局 使得网站看起来更加吸引人并且具备更好的可读性 本书将通过一系列深入浅出的方式 带你从入门到精通CSS 探索We
  • React 笔记 jsx

    严格约定 React 组件必须以 大写字母开头 而 HTML 标签则必须是小写字母 React JSX JSX 是由 React 推广的 JavaScript 语法扩展 用于表达组件的 特殊语法的 js 函数 要求标签必须闭合 返回的组件必
  • <a>标签的超链接前面会自动加上当前(网站)地址

    当前 网站 地址是 fyh com 在代码里写 a 标签时 会自动在链接前添加 fyh com 例如写如下代码 a href www baidu com baidu a 在浏览器中点击链接会跳转至 fyh com www baidu com
  • 如何提高React组件的渲染效率的?在React中如何避免不必要的render?

    面试官 说说你是如何提高组件的渲染效率的 在React中如何避免不必要的render 一 是什么 react 基于虚拟 DOM 和高效 Diff 算法的完美配合 实现了对 DOM 最小粒度的更新 大多数情况下 React 对 DOM 的渲染
  • 课设:NFA确定化和最小化程序的设计与实现(html+css+js实现)

    文章目录 问题描述 待解决问题 1 如何存储NFA或者是DFA 2 NFA多初态问题 3 子集化过程思路 4 分割法过程思路 使用方法 下载链接 问题描述

随机推荐

  • React +TS实现拖拽列表

    使用React TS编写逻辑代码 less编写样式代码 不依赖第三方库 开箱即用 最近写的拖拽组件 分享给大家 直接上代码 首先看看如何使用 自己定义的组件需要包裹在DragList Item组件中 import DragList from
  • 遍历磁盘根目录

    for char i A i lt Z i 遍历所有磁盘 char dir 20 i 组成磁盘名称 char path 100 定义文件路径 UINT type GetDriveType dir 获取磁盘类型 if type DRIVE F
  • 网络与信息安全基础知识--网络安全

    说在前面 本系列文章专注于软考备考复习内容梳理 文章内容是对教材中知识点和考点的提炼 备考过程中可以有针对的进行复习 减少阅读量 有的放矢 导航目录 一 网络安全概述 二 网络的信息安全 1 信息的存储安全 2 信息的传输安全 三 防火墙技
  • Mac安装Netcat教程

    Netcat可以用于测试通信连接 Mac安装Netcat方式 打开终端输入 brew install netcat 安装好以后测试 输入 nc 可以看到是这样的 itzhuzhu itzhuzhudeMacBook Pro brew ins
  • 五、【服务器】基本概念-1

    服务器标准 ATCA AdvancedTelecom Computing Architecture 国际标准 ATCA脱胎于在电信 航天 工业控制 医疗器械 智能交通 军事装备等领域应用广泛的新一代主流工业计算技术 CompactPCI标准
  • 网络系统实现技术之IPX与SPX

    IPX SPX Novell NetWare网络 Novell公司为适应网络发展 将主机网络转换为PC网络 开发了Novell NetWare网络系统 该系统中基于客户机 服务器模式 以普通PC机做为客户机 以性能强大的服务器做为服务器 为
  • 什么是算法?

    什么是算法 当人们提到 算法 一词 往往就会把它们当成专属于 人工智能 的范畴 很多专业的计算机人士也是 提起算法就头疼 不知道如何学习算法 慢慢的对算法就会失去兴趣 算法不仅仅是计算机行业特有的 在我们的生活中也处处存在着算法 算法是专注
  • 【积跬步以至千里】Windows无法访问指定设备,路径或文件,您可能没有合适的权限访问

    一 问题描述 今天在使用电脑时突然出现如下状况 然后我打开用户权限发现了原来是权限的问题 我点击编辑 依然不管事 那怎么处理呢 二 解决办法 1 方法一 单独设置 1 在无法打开的文件 文件夹上单击鼠标右键 选择 属性 2 切换到 安全 选
  • __int64、ULONGLONG格式化输出

    Tips 打印日志信息的时候出现的问题 虽然很小 也算提个醒 Code 不考虑溢出的情况 ULONGLONG n1 100 printf d n n1 int64 n2 100 printf d n n2 此处是个坑 printf d d
  • 华为推出手机系统云翻新服务:什么是云翻新?如何使用?

    华为手机系统云翻新是华为推出的一项功能 旨在通过云服务提供系统翻新的服务 它可以帮助用户对手机的系统进行优化和更新 以提高手机的性能和流畅度 具体而言 华为手机系统云翻新功能提供了免费的云空间 用户可以将手机中的系统数据备份到云端 并进行系
  • 一位程序员使用M1 Mac的感受

    作为一个window的java开发者 虽然现在window高配置不卡 但是身边的高级开发者都是使用苹果开发 并且给予高度评价 这里也抱着学习的态度去尝试安利一台MAC作为开发 所以去苹果官网看了一下 但是这次苹果出了一个全新的M1芯片 我在
  • STM32网络通信Web Server中SSI和CGI的应用

    介绍 最近由于项目功能需要 开始研究STM32 WebServer通信以及SSI和CGI应用方法 项目结束后 主要总结浏览器与STM32之间进行通行 STM32作为服务器而浏览器做为客户端进行通行 文件介绍 此部分的代码是根据ST官方的We
  • 免费分享一套 SpringBoot + Vue的排课/选课管理系统,挺漂亮的

    大家好 我是锋哥 看到一个不错的SpringBoot Vue 的排课 选课管理系统 分享下哈 项目介绍 近年来 随着网络学校规模的逐渐增大 人工书写数据已经不能够处理如此庞大的数据 为了更好的适应信息时代的高效性 一个利用计算机来实现学生信
  • Python——requests

    requests是python实现的简单易用的HTTP库 使用起来比urllib简洁很多 因为是第三方库 所以使用前需要cmd安装 pip install requests 安装完成后import一下 正常则说明可以开始使用了 基本用法 r
  • c/c++ 计算字符数组/字符串长度

    1 自定义函数求长度 2 使用strlen 函数 3 使用sizeof 操作符 4 使用length 函数 利用自定义函数的方法 int cont str char s int i 0 while str i 0 return i 利用st
  • 已解决【partially initialized module ‘cv2‘ has no attribute ‘gapi_wip_gst_GStreamerPipeline‘】

    已解决 partially initialized module cv2 has no attribute gapi wip gst GStreamerPipeline 在尝试了几乎所有网上能找到的办法之后 本来已经放弃了 但是过了几天抱着
  • VAN:Visual Attention Network

    Visual Attention Network Submitted on 20 Feb 2022 v1 last revised 11 Jul 2022 this version v5 Computer Vision and Patter
  • 微服务zipkin与turbine同时使用遇到的问题

    最近整合zipkin的时候遇到的问题 如果打开turbine监控时 每个turbine刷新周期内都会有rxjava的调用被zipkin捕获到 由于zipkin中的数据是通过Spring cloud sleuth上传的 查阅https clo
  • 数字化转型下数据库面临的12个挑战

    数字化及数字化转型 是近些年来非常火热的话题 本文将从这一角度切入 谈谈数字化场景下对数据库发展趋势带来的影响 1 数据 数字化 数字化转型 数据 是数字化实现的新引擎 数据是企业开展数字化创新和构建企业数字化基因的核心要素 通过对于服务对
  • React--井字棋小游戏

    安装较新版本的node js 这里使用的是v15 0 1 用以记录React学习笔记 1 搭建本地开发环境 在想要创建项目的文件夹下输入cmd 回车 输入命令npx create react app my app等待项目初始化 等待一段时间