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

React井字棋问题:获胜后需额外一步才检测出赢家

问题分析与修复

问题根源

你遇到的延迟问题是因为React的状态更新是异步批量执行的:在handleClick中调用setSquares(newSquares)后,组件状态里的squares不会立刻更新。此时立刻调用checkWinner(),它读取的还是更新前的旧squares数组,自然检测不到当前这一步产生的赢家。只有等下一次状态更新(玩家再走一步),squares才会更新到最新值,checkWinner才能检测到之前的获胜组合。

修复方案

修改checkWinner函数,让它直接接收最新的棋盘状态(也就是你刚刚修改的newSquares),而不是依赖还没更新的组件状态,同时简化冗余的状态逻辑:

步骤1:重构checkWinner函数

const checkWinner = (currentSquares) => {
  if (winner !== null) return;
  const winningCombinations = [
    [0, 1, 2], // horizontal
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6], // vertical
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8], // diagonal
    [2, 4, 6],
  ];

  // 用some代替forEach,找到赢家后立刻停止遍历,避免重复执行
  winningCombinations.some((combination) => {
    const [a, b, c] = combination;
    if (
      currentSquares[a] === currentSquares[b] &&
      currentSquares[b] === currentSquares[c] &&
      currentSquares[a] !== null
    ) {
      setWinner(currentSquares[a] === "X");
      return true; // 终止遍历
    }
    return false;
  });
};

步骤2:简化handleClick逻辑,移除冗余状态

squares数组里的null已经可以标记未点击的格子,不需要单独维护isClicked数组,直接删除相关状态,并在点击时增加赢家判断,禁止后续操作:

const handleClick = (index) => {
  // 已点击或已有赢家时,禁止操作
  if (squares[index] !== null || winner !== null) return;

  const newSquares = [...squares];
  newSquares[index] = turnX ? "X" : "O";

  setSquares(newSquares);
  checkWinner(newSquares); // 传入最新的棋盘状态
  setTurnX(!turnX);
};

步骤3:删除冗余代码

删掉match函数和isClicked相关的状态声明与更新逻辑,减少不必要的状态维护。

优化后完整代码

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

function TicTacToe() {
  const [squares, setSquares] = useState(Array(9).fill(null));
  const [turnX, setTurnX] = useState(true);
  const [winner, setWinner] = useState(null);

  useEffect(() => {
    if (winner !== null) {
      console.log(winner ? "X is the winner" : "O is the winner");
    }
  }, [winner]);

  const checkWinner = (currentSquares) => {
    if (winner !== null) return;
    const winningCombinations = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6],
    ];

    winningCombinations.some((combination) => {
      const [a, b, c] = combination;
      if (
        currentSquares[a] === currentSquares[b] &&
        currentSquares[b] === currentSquares[c] &&
        currentSquares[a] !== null
      ) {
        setWinner(currentSquares[a] === "X");
        return true;
      }
      return false;
    });
  };

  const handleClick = (index) => {
    if (squares[index] !== null || winner !== null) return;

    const newSquares = [...squares];
    newSquares[index] = turnX ? "X" : "O";

    setSquares(newSquares);
    checkWinner(newSquares);
    setTurnX(!turnX);
  };

  return (
    <div>
      <div className="board">
        {squares.map((square, index) => (
          <div
            className="square"
            id={`_${Math.floor(index / 3)}${index % 3}`}
            key={index}
            onClick={() => handleClick(index)}
          >
            {square}
          </div>
        ))}
      </div>
    </div>
  );
}

export default TicTacToe;

额外建议

  • 可以添加平局判断逻辑:当squares数组中没有null且winner仍为null时,提示平局。
  • 可以在页面上直接显示赢家信息,而不只是在控制台打印。

内容的提问来源于stack exchange,提问作者i-am-afk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 20:50:54