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

