当前位置: 首页 > 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.dtcms.com/a/292326.html

相关文章:

  • ZLMediaKit 入门
  • 第12天 | openGauss逻辑结构:模式管理
  • Java 大视界 -- Java 大数据在智能医疗医疗设备维护与管理中的应用(358)
  • 25. K 个一组翻转链表
  • Odoo:免费开源的金属制品行业ERP管理软件
  • React 面试题库
  • 写个 flask todo app,简洁,实用
  • calibrate_hand_eye (CalibDataID, Errors)
  • 2025年远程桌面软件深度评测:ToDesk、向日葵、TeamViewer全方位对比分析
  • C++学习——内联、C++11中的auto、for循环、nullptr
  • Windows Cmake Vs2017/2010 编译安装Protobuf
  • 【计算机网络】第五章:传输层
  • 双向链表详解及实现
  • 解锁高品质音频体验:探索音频质量评估与测试的科学之道
  • Vibe Coding:人工智能 + 语音 = 新型开发者工作流
  • Thingsboard是什么?跟LoRaWAN 是什么关系?
  • 图像基础:从像素到 OpenCV 的入门指南
  • 【加解密与C】Rot系列(四)RotSpecial
  • 【windows修复】解决windows10,没有【相机] 功能问题
  • 2025易支付插件/度小满/拉卡拉缴费易/嘉联/海科码钱/富友 支付量身定制的易支付插件优化方案
  • Jupyter Notebook |使用清华源下载安装
  • Final_基于时序数据的回归预测
  • ABP VNext + FluentMigrator:数据库迁移管理
  • AI智能体“上下文工程”实践:来自 Manus 项目的经验总结
  • 【ArcGIS Pro】设置临时存储文件夹(计算缓存数据存放位置)
  • RedisJSON 指令精讲JSON.STRLEN 高效统计字符串长度
  • OpenMVG OpenMVS 安装全流程常见问题与解决方法总结
  • almalinux9.6系统-基础环境准备
  • 【大气反演模型CIF第二期】通过 Docker 容器快速部署和运行 CIF 系统
  • 基于eBPF的Kubernetes网络故障自愈系统设计与实现