React版井字棋函数调用失效问题求助
问题分析与修复方案
你的代码里有几个关键问题导致点击事件不触发,同时还有不符合React编程范式的写法,以下是具体问题和修复方式:
核心问题点
- 事件绑定错误:React中事件采用驼峰命名,比如
onClick,而非原生的onclick,原来的onclick绑定不会被React识别。 - 类名属性错误:React元素的类名要用
className,不是class。 this指向错误:在箭头函数中this不是当前DOM元素,无法正确传递给makeMove。- 状态未初始化:
useState没有设置初始值,导致初始状态为undefined,逻辑判断失效。 - 直接操作DOM:React是状态驱动视图,直接修改
innerHTML或操作DOM元素违背这个理念,容易引发状态不一致。 - 错误的DOM结构:组件return中嵌套
<body>标签不符合规范,React组件应作为页面的一部分渲染,不应包含根级的body。 - 初始化时机错误:依赖
body.onload执行setup不可靠,React中应使用useEffect在组件挂载时初始化。
修复后的完整代码
TicTacToe.js
import React, { useState, useEffect } from 'react' import './TicTacToe.css' const TicTacToe = () => { const x = '10006'; const o = '9898'; // 初始化状态,给默认值 const [firstPlayerMove, setFirstPlayerMove] = useState(true); const [gameWon, setGameWon] = useState(false); const [moves, setMoves] = useState(0); // 新增棋盘状态,存储每个格子的内容(''/X/O) const [board, setBoard] = useState(Array(9).fill('')); const [message, setMessage] = useState("It's X's turn."); // 组件挂载时执行初始化 useEffect(() => { setup(); }, []); const setup = () => { setFirstPlayerMove(true); setGameWon(false); setMoves(0); setBoard(Array(9).fill('')); setMessage("It's X's turn."); } const restart = () => { setup(); } const makeMove = (index) => { if (gameWon || board[index] !== '') return; // 更新棋盘状态 const newBoard = [...board]; newBoard[index] = firstPlayerMove ? String.fromCharCode(x) : String.fromCharCode(o); setBoard(newBoard); const newMoves = moves + 1; setMoves(newMoves); // 检查胜负 if (checkWin(newBoard)) { setMessage(firstPlayerMove ? "Player 1 (X) won." : "Player 2 (O) won."); setGameWon(true); return; } // 检查平局 if (newMoves === 9) { setMessage("Draw."); return; } // 切换玩家 setFirstPlayerMove(!firstPlayerMove); setMessage(firstPlayerMove ? "It's O's turn." : "It's X's turn."); } const checkWin = (currentBoard) => { const winningPatterns = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (const pattern of winningPatterns) { const [a, b, c] = pattern; if (currentBoard[a] && currentBoard[a] === currentBoard[b] && currentBoard[a] === currentBoard[c]) { return true; } } return false; } // 渲染棋盘行 const renderRow = (startIndex) => ( <div className="row"> {[0,1,2].map(offset => { const index = startIndex + offset; return ( <div key={index} onClick={() => makeMove(index)} className="tile" > {board[index]} </div> ) })} </div> ) return ( <div className="tic-tac-toe-container"> <button onClick={restart} className="start">Restart</button> <div className="board"> {renderRow(0)} {renderRow(3)} {renderRow(6)} </div> <div className="msg">{message}</div> </div> ) } export default TicTacToe
关键优化点说明
- 状态驱动视图:用
board数组存储每个格子的内容,React自动渲染,不再直接操作DOM。 - 正确的事件绑定:使用
onClick替代onclick,className替代class。 - 传递索引而非DOM元素:点击格子时传递对应的索引,通过状态修改棋盘,避免直接操作DOM。
- useEffect初始化:组件挂载时自动执行
setup,替代原来的body.onload。 - 消息状态化:用
message状态存储提示信息,由React渲染,不再操作innerHTML。 - 简化胜利判断:直接基于当前棋盘状态判断,无需再从DOM读取内容。
内容的提问来源于stack exchange,提问作者Bruhloon
相关产品推荐
相关产品推荐

