You need to enable JavaScript to run this app. React函数组件结合useState实现井字棋点击落子,如何更新合并棋盘状态?
优惠活动
大模型
产品
解决方案
定价
更多

React函数组件结合useState实现井字棋点击落子,如何更新合并棋盘状态?

实现思路与代码修正

你当前的实现犯了React开发的典型反模式:完全违背了React「状态驱动视图」的核心设计原则。你现在手动通过DOM选择器修改innerHTML的写法,本质是把React当成了普通DOM操作库来用,不仅会出现状态和视图不同步的bug,也完全达不到课程作业对React函数组件、Hook用法的考察要求。

现有代码的具体问题

  • 状态结构设计不合理:你把所有点击过的方块id存在数组里,没有和9个方块的渲染位置做一一映射,后续要把状态转成视图内容需要额外做遍历匹配,这是你觉得“状态合并困难”的根源
  • 直接操作原生DOM:单独写的ChangeBoard组件通过document.getElementsByClassName手动改元素内容,完全脱离React的虚拟DOM管控,后续一旦触发重渲染,你手动改的内容很可能被React的diff逻辑覆盖
  • 事件绑定逻辑不合理:把点击事件绑在最外层容器上靠事件委托取目标id,很容易出现点到棋盘外区域的误触发,也没有把方块的点击行为和自身的索引绑定

正确实现逻辑

不需要再写任何手动修改DOM的代码,核心要做的是把整个棋盘的展示状态完全上提到App组件,让每个方块的渲染内容完全由传入的状态决定:

  1. 调整状态结构:初始状态直接设置为长度为9的空数组,每个位置的元素对应一个方块要显示的内容,替代原来存所有点击id的数组结构
  2. 写状态更新逻辑:每次点击方块时,基于之前的旧棋盘状态做不可变更新(复制旧数组、修改对应位置的值为'X'、返回新数组),不要直接修改原状态
  3. 改造棋盘组件:让棋盘组件接收当前的棋盘状态、点击回调作为props,循环渲染9个方块,每个方块的内容直接从传入的状态里读取,不需要额外的修改DOM操作

修正后的完整代码

App 组件

import React, { useState } from 'react';
import './App.css';
import { MakeBoard } from './Board.js';

function App() {
  // 初始化9个空值的棋盘状态,每个索引对应一个方块的显示内容
  const [board, setBoard] = useState(Array(9).fill(''));

  // 方块点击处理函数,接收被点击方块的索引
  const handleBoxClick = (boxIndex) => {
    // 已填充内容的方块不响应重复点击
    if (board[boxIndex]) return;
    setBoard(prevBoard => {
      // 复制旧状态生成新数组,遵守React状态不可变要求
      const newBoard = [...prevBoard];
      newBoard[boxIndex] = 'X';
      return newBoard;
    })
  }

  return (
    <div className="app">
      {/* 把棋盘状态、点击回调作为props传给棋盘组件 */}
      <MakeBoard board={board} onBoxClick={handleBoxClick} />
    </div>
  );
}

export default App;

Board 组件

import React from 'react';
import './Board.css';

// 之前写的ChangeBoard组件完全删除,不需要任何手动改DOM的逻辑
export function MakeBoard({ board, onBoxClick }) {
  return (
    <div className="board">
      {/* 循环渲染方块,替代原来写死的9个div */}
      {board.map((boxContent, index) => (
        <div 
          className="box" 
          key={index}
          onClick={() => onBoxClick(index)}
        >
          {/* 直接根据状态渲染方块内容,无任何DOM操作 */}
          {boxContent}
        </div>
      ))}
    </div>
  )
}

关于你之前卡壳的“状态合并”问题:现在的实现里根本不需要手动做状态和视图的合并,只要你每次更新状态时返回一个新的棋盘数组,React就会自动diff新旧状态的差异,重新渲染所有方块的内容,把有'X'的位置展示出来,直到9个方块全部填满。


内容的提问来源于stack exchange,提问作者In Doing It

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.03 07:45:04