当前位置: 首页 > news >正文

使用react编写一个简单的井字棋游戏

用React实现井棋棋游戏:从状态管理到时间旅行功能详解

引言

井字棋是学习React状态管理和组件交互的经典案例。本文将逐行解析一个完整实现,包含历史回溯功能胜者判定逻辑,即使是React初学者也能彻底理解。


核心组件架构
Game组件
Board棋盘
历史记录
Square格子

1. Square组件:最基础的交互单元
function Square({ value, onSquareClick }) {return (<button onClick={onSquareClick}>{value} </button>);
}
  • 功能解析:
    • value:接收父组件传递的X/O/null
    • onSquareClick:点击时触发父组件的回调函数
    • 设计要点:组件本身不维护状态,由父组件完全控制(受控组件)

2. Board组件:游戏逻辑核心
function Board({ xIsNext, square, onPlay }) {// 处理格子点击事件function handleClick(i) {// 存在胜者或格子已被占用时阻止操作if (calculateWinner(square) || square[i]) return;const nextSquares = square.slice(); // 浅拷贝当前棋盘状态nextSquares[i] = xIsNext ? 'X' : 'O'; // 设置当前玩家符号onPlay(nextSquares); // 传递新状态给父组件}// 判断胜负状态const winner = calculateWinner(square);const status = winner ? `Winner: ${winner}` : `Next player: ${xIsNext ? 'X' : 'O'}`;// 渲染9宫格棋盘return (<><div className="status">{status}</div>{[0, 3, 6].map((rowStart) => (<div key={rowStart} className="board-row">{[0, 1, 2].map((offset) => {const index = rowStart + offset;return (<Square key={index}value={square[index]}onSquareClick={() => handleClick(index)}/>);})}</div>))}</>);
}

关键逻辑解析

  1. 点击处理流程
    • 通过square[i]检查格子是否为空
    • 使用slice()浅拷贝数组避免直接修改状态
    • 根据xIsNext决定放置XO
  2. 状态提升(Lifting State Up)
    • 通过onPlay将新状态回调给Game组件
    • 符合React单向数据流原则

3. Game组件:全局状态管理
export default function Game() {// 历史记录保存所有棋盘状态const [history, setHistory] = useState([Array(9).fill(null)]);// 当前查看的历史步骤const [currentMove, setCurrentMove] = useState(0);// 派生状态const xIsNext = currentMove % 2 === 0;const currentSquare = history[currentMove];// 处理棋盘状态更新function handlePlay(nextSquares) {// 裁剪历史记录(用于时间旅行后重新落子)const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];setHistory(nextHistory);setCurrentMove(nextHistory.length - 1);}// 时间旅行:跳转到指定历史步骤function jumpTo(nextMove) {setCurrentMove(nextMove);}// 生成历史步骤按钮const moves = history.map((squares, move) => {const desc = move > 0 ? `Go to move #${move}` : 'Go to game start';return (<li key={move}><button onClick={() => jumpTo(move)}>{desc}</button></li>);});return (<div className="game"><div className="game-board"><Board xIsNext={xIsNext}square={currentSquare}onPlay={handlePlay}/></div><div className="game-info"><ol>{moves}</ol></div></div>);
}

核心机制解析

  1. 历史记录管理
    • history:存储棋盘状态数组的数组
    • currentMove:当前展示的历史步骤索引
  2. 时间旅行实现原理
    • 点击历史按钮时jumpTo修改currentMove
    • 渲染对应历史索引的状态:currentSquare = history[currentMove]
    • 重新落子时裁剪历史分支
      history.slice(0, currentMove + 1)确保历史线性的正确性

4. 胜者判定算法
function calculateWinner(squares) {// 8种获胜组合(三行/三列/两对角线)const lines = [[0, 1, 2], [3, 4, 5], [6, 7, 8], // 行[0, 3, 6], [1, 4, 7], [2, 5, 8], // 列[0, 4, 8], [2, 4, 6]             // 对角线];for (const [a, b, c] of lines) {if (squares[a] &&squares[a] === squares[b] &&squares[a] === squares[c]) {return squares[a]; // 返回胜者符号'X'/'O'}}return null; // 无胜者
}

算法特点

  • 时间复杂度:O(1)(固定8种判断)
  • 返回值'X'/'O'(胜者)或null(未结束)
  • 空值检查squares[a] &&防止访问未填充格子

总结
  1. 组件层级清晰

    • Game(状态容器)→ Board(逻辑核心)→ Square(UI展示)
  2. 符合React最佳实践

    • 状态提升:子组件通过回调修改父组件状态
    • 不可变数据:使用slice()创建新数组
    • 派生状态:xIsNextcurrentMove计算得出
  3. 进阶功能实现

    • 时间旅行:通过历史索引切换棋盘状态
    • 历史裁剪:重新落子时自动清理分叉历史
  4. 可扩展方向

    当前实现
    添加移动高亮
    显示每步坐标
    平局判定

通过这个实现,我们展示了React的核心概念:组件化状态提升不可变数据派生状态。即使从未接触过React,按照组件分解的思维方式也能逐步理解整个应用的数据流动和交互逻辑。

http://www.lryc.cn/news/596409.html

相关文章:

  • 17.VRRP技术
  • 接口自动化测试种涉及到接口依赖怎么办?
  • 微调大语言模型(LLM)有多难?
  • Google Gemini 体验
  • 深入解析Hadoop中的推测执行:原理、算法与策略
  • kafka查看消息的具体内容 kafka-dump-log.sh
  • SDC命令详解:使用set_min_library命令进行约束
  • Unity笔记——事件中心
  • HTB赛季8靶场 - Mirage
  • 风险识别清单:构建动态化的风险管理体系
  • Java函数式编程深度解析:从基础到高阶应用
  • 技能系统详解(4)——运动表现
  • 哔哩哔哩视觉算法面试30问全景精解
  • 钢铁逆行者:Deepoc具身智能如何重塑消防机器人的“火场直觉”
  • 【中文翻译】SmolVLA:面向低成本高效机器人的视觉-语言-动作模型
  • Vue 3 响应式系统中的 effectScope、watchEffect、effect 和 watch 详解
  • 如何将iPad中的视频传输到电脑(6种简单方法)
  • 单片机学习笔记.单总线one-wire协议(这里以普中开发板DS18B20为例)
  • rabbitmq 03
  • uniapp 报错 Not found ... at view.umd.min.js:1的问题
  • LWIP学习记录2——MAC内核
  • Linux系统安装Bash自动补全(bash-completion)
  • 基于SpringBoot+Uniapp的非遗文化宣传小程序(AI问答、协同过滤算法、Echarts图形化分析)
  • uniapp请求封装上传
  • 最新植物大战僵尸杂交版最新版本2.5.1版,内置触屏+加速+全屏,附PC+安卓+iOS最全安装教程!
  • C#文件操作(创建、读取、修改)
  • Java学习-------事务失效
  • 从“点状用例”到“质量生态”:现代软件测试的演进、困局与破局
  • Vue3 学习教程,从入门到精通,Vue3 循环语句(`v-for`)语法知识点与案例详解(13)
  • C# 属性