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
相关产品推荐
相关产品推荐

