You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React开发井字棋(Tic-tac-toe)无法限制方格重复点击如何解决

问题原因与修复方案

核心问题

  • 点击事件目标获取错误:你在点击落子时直接向square div中插入了<h1>元素,第二次点击时大概率命中的是内部的h1元素,h1没有设置id属性,导致e.target.id为空,e.target.id-1得到NaN,board[NaN]永远为undefined,直接绕过了方格已占用的判断逻辑。
  • 状态更新不符合React规范:更新board数组时直接修改原数组引用,React浅比较检测不到数组变化,不会触发组件重渲染,会出现状态和视图不一致的问题。
  • 视图未与状态绑定:方格内容直接通过手动操作DOM修改,没有从board状态派生,不符合React声明式开发的逻辑,容易出现数据不同步。

修复后完整代码

import "./styles.css";
import { useState } from "react";

export default function App() {
  const [player, setPlayer] = useState("X");
  const [gameOver, setGameOver] = useState(false);
  // 初始化数组填充undefined,避免稀疏数组问题
  const [board, setBoard] = useState(new Array(9).fill(undefined));
 
  const checkGameOver = () => { 
      const square_a = board[0];
      const square_b = board[1];
      const square_c = board[2];
      //vertical win check
      if(square_a === board[3] &&  square_a === board[6] && square_a !== undefined) {return true;}
      if(square_b === board[4] &&  square_b === board[7] && square_b !== undefined) {return true;}
      if(square_c === board[5] &&  square_c === board[8] && square_c !== undefined) {return true;}
      //diagonal win check
      if(square_a === board[4] &&  square_a === board[8] && square_a !== undefined) {return true;}
      if(square_c === board[4] &&  square_c === board[6] && square_c !== undefined) {return true;}
      //horizontal win check
      if(board[0] === board[1] && board[1] === board[2] && board[0] !== undefined) {return true;}
      if(board[3] === board[4] && board[4] === board[5] && board[3] !== undefined) {return true;}
      if(board[6] === board[7] && board[7] === board[8] && board[6] !== undefined) {return true;}
      
      return false;
  }

  // 直接传入点击的方格索引,不用依赖事件目标拿id
  const handleMove = (index) => {
    // 游戏结束直接返回,不处理
    if(gameOver) return;
    // 判断方格已占用
    if(board[index] !== undefined) {
      alert("Can't play on this square");
      return;
    }
    // 创建新数组,不修改原状态,符合React不可变原则
    const newBoard = [...board];
    newBoard[index] = player;
    setBoard(newBoard);
    // 判断游戏是否结束
    if(checkGameOver()) {
      setGameOver(true);
      return;
    }
    // 切换玩家
    setPlayer(player === "X" ? "O" : "X");
  }
  
  return (
    <>
      {!gameOver && <div className="background-div">
        <div className = "board">
          {/* 遍历board数组渲染方格,内容直接从状态取,不用手动操作DOM */}
          {board.map((val, index) => (
            <div 
              key={index}
              className="square-div" 
              onClick={() => handleMove(index)}
            >
              {val && <h1>{val}</h1>}
            </div>
          ))}
        </div>  
      </div>}
      {gameOver && <h1>Game Over</h1>}
    </>
  );
}

内容的提问来源于stack exchange,提问作者youngprogrammer1997

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 01:15:03