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

React井字棋教程挑战报错:handleClick is not a function

React井字棋游戏循环生成Square组件报错问题

跟着React官方教程实现井字棋游戏时,尝试用循环生成Square组件替代硬编码,编写CreateSquares组件后点击方块出现handleClick is not a function错误,代码及报错信息如下:

代码

import { useState } from "react";

function Square({ value, onSquareClick }) {
    return (
        <button className="square" onClick={onSquareClick} >{value}</button>
    );
}

function CreateSquares(squares, handleClick) {

    const data = [];
    for (let i = 0; i < 3; i++) {
        const rows = [];
        for (let j = 0; j < 3; j++) {
            rows.push(
                <Square value={squares[j]} onSquareClick={() => handleClick(j)} key={j} />
            );
        }
        data.push(
            <div className="board-row" key={i}>{rows}</div>
        );
    }
    return data;
}

function Board({ xIsNext, squares, onPlay }) {

    let winner = calculateWinner(squares);

    function handleClick(index) {
        if (squares[index] || winner) {
            return;
        }
        const nextSquares = squares.slice();

        nextSquares[index] = xIsNext ? 'X' : 'O';

        onPlay(nextSquares);
    }
    let status = winner ? "Winner: " + winner : "Next Player: " + (xIsNext ? "X" : "O");

    return (
        <>
            <div className="status">{status}</div>
            <CreateSquares squares={squares} handleClick={handleClick} />
            {/* <div className="board-row">
                <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
                <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
                <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
            </div>
            <div className="board-row">
                <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
                <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
                <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
            </div>
            <div className="board-row">
                <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
                <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
                <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
            </div> */}
        </>
    );
}

export default function Game() {
    const [history, setHistory] = useState([Array(9).fill(null)]);
    const [currentMove, setCurrentMove] = useState(0);
    const xIsNext = currentMove % 2 === 0;
    const currentSquares = history[currentMove];

    function handlePlay(nextSquares) {
        const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
        setHistory(nextHistory);
        setCurrentMove(nextHistory.length - 1);
    }

    function jumpTo(nextMove) {
        setCurrentMove(nextMove);
    }

    const moves = history.map((squares, move) => {
        let description = move > 0 ? 'Go to move #' + move : "Go to game start";

        return (
            <li key={move}>
                <button onClick={() => { jumpTo(move) }}>{description}</button>
            </li>
        );
    });

    return (
        <div className="game">
            <div className="game-board">
                <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
            </div>
            <div className="game-info">
                <ol>{moves}</ol>
            </div>
        </div>
    );
}

function calculateWinner(squares) {
    const lines = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6]
    ];

    let len = lines.length;
    for (let i = 0; i < len; i++) {
        const [a, b, c] = lines[i];
        if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
            return squares[a];
        }
    }

    return null;
}

报错信息

handleClick is not a function
TypeError: handleClick is not a function
at onSquareClick (http://localhost:3000/main.9d076070d9e17e2c1932.hot-update.js:51:30)
at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:10826:18)
at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:10870:20)
at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:10927:35)
at invokeGuardedCallbackAndCatchFirstError (http://localhost:3000/static/js/bundle.js:10941:29)
at executeDispatch (http://localhost:3000/static/js/bundle.js:15085:7)
at processDispatchQueueItemsInOrder (http://localhost:3000/static/js/bundle.js:15111:11)
at processDispatchQueue (http://localhost:3000/static/js/bundle.js:15122:9)
at dispatchEventsForPlugins (http://localhost:3000/static/js/bundle.js:15131:7)
at http://localhost:3000/static/js/bundle.js:15291:16

问题原因及修复方案

1. 组件参数接收错误

React组件的参数是一个props对象,而非单独的参数列表。你的CreateSquares函数定义为function CreateSquares(squares, handleClick),但实际调用时传入的squares={squares} handleClick={handleClick}会被包裹在props对象里,导致squares参数实际是props对象,handleClick参数为undefined,触发点击时就会报错。

修改方式:将函数参数改为解构props对象:

function CreateSquares({ squares, handleClick }) {
  // ... 原有逻辑
}

2. 方块索引计算错误

当前循环中,每一行的方块都使用j作为索引(0-2),导致所有行都取squares数组的前3个元素,点击时也只会触发0-2的索引,不符合井字棋9个位置的逻辑。需要计算正确的索引:i*3 + j(i是行号,j是列号)。

修改后的CreateSquares组件完整代码:

function CreateSquares({ squares, handleClick }) {
    const data = [];
    for (let i = 0; i < 3; i++) {
        const rows = [];
        for (let j = 0; j < 3; j++) {
            const index = i * 3 + j;
            rows.push(
                <Square value={squares[index]} onSquareClick={() => handleClick(index)} key={index} />
            );
        }
        data.push(
            <div className="board-row" key={i}>{rows}</div>
        );
    }
    return data;
}

将上述两处修改后,点击方块就能正常触发handleClick函数,且每个方块对应正确的squares数组位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:04:53