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

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

关键优化点说明

  1. 状态驱动视图:用board数组存储每个格子的内容,React自动渲染,不再直接操作DOM。
  2. 正确的事件绑定:使用onClick替代onclick,className替代class。
  3. 传递索引而非DOM元素:点击格子时传递对应的索引,通过状态修改棋盘,避免直接操作DOM。
  4. useEffect初始化:组件挂载时自动执行setup,替代原来的body.onload。
  5. 消息状态化:用message状态存储提示信息,由React渲染,不再操作innerHTML。
  6. 简化胜利判断:直接基于当前棋盘状态判断,无需再从DOM读取内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 10:15:34