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

React开发数独应用时useEffect内生成函数被调用两次如何解决

问题原因

  1. 核心逻辑问题:你将row、previuosRows、sudoku三个变量定义在了App组件的顶层作用域,而不是createSudokValues函数内部,同时函数执行时没有重置这三个变量的值。多次调用createSudokValues时,会在上一次生成的数组基础上继续追加元素,导致最终数组长度超过预期的81。
  2. 重复调用问题:你当前代码里一共会调用两次createSudokValues:第一次是在空依赖的useEffect回调中,第二次是在JSX渲染时的map逻辑前。如果你使用的是React18+且项目开启了严格模式(默认创建的项目都会开启),开发环境下useEffect会额外多执行一次,进一步增加调用次数。
  3. 冗余逻辑问题:内层循环中while (row.length <=9)的判断完全多余,每次内层循环只会执行一次push后就break,完全可以直接去掉while语句。

解决方法

  1. 将三个临时变量的声明移动到createSudokValues函数内部,确保每次调用函数都会重新初始化变量,不会复用上次的结果。
  2. 用useState存储生成的数独数组,仅在组件初始化时生成一次,避免重复调用生成函数。
  3. 删掉冗余的while判断逻辑。

优化后代码

import React, { useEffect, useState } from 'react';
import '../App.css';
import Square from './Square';

function App() {
    type SquareType = {
        id: number;
        digit: number;
        index: number;
        shown: boolean;
    }
    // 用state存储生成的数独数组
    const [sudokuList, setSudokuList] = useState<SquareType[]>([]);
    const possibleOptionsForDigit = [1, 2, 3, 4, 5, 6, 7, 8, 9];

    function generateRandomArrayIndex(unusedDigits: Array<number> ) {
        return Math.floor(Math.random() * unusedDigits.length);
    }

    function unusedDigitInRowAndColumn( sudoku: Array<SquareType>, row: Array<SquareType>, columnIndex: number ) {
        const digitsExistingInRow = row.map(square => square.digit);
        const unusedDigitsInRow = possibleOptionsForDigit.filter(digit => !digitsExistingInRow.includes(digit));
    
        const digitsExistingInColumn = sudoku.filter(square => square.index === columnIndex).map(square => square.digit);
        const unusedDigitsInColumn = possibleOptionsForDigit.filter(digit => !digitsExistingInColumn.includes(digit));
    
        const unusedDigits = unusedDigitsInRow.filter(digit => unusedDigitsInColumn.includes(digit)); 
        return unusedDigits[generateRandomArrayIndex(unusedDigits)];
    }

    function createSudokValues() {
        // 临时变量移动到函数内部
        let row: Array<SquareType> = [];
        let previuosRows: Array<SquareType> = [];
        let sudoku: Array<SquareType> = [];
        let idIncremented: number = 0;
        let generatedUnusedDigit: number = 0;
        for ( let y = 1; y <= 9; y++ ) {
            for ( let columnIndex = 1; columnIndex <= 9; columnIndex++ ) {
                // 去掉冗余的while判断
                generatedUnusedDigit = unusedDigitInRowAndColumn(sudoku, row, columnIndex);
                row.push(
                    {
                        id: idIncremented,
                        digit: generatedUnusedDigit,
                        index: columnIndex,
                        shown: true
                    }
                );
                idIncremented++;
            }
            previuosRows = [ ...sudoku];
            sudoku = [ ...previuosRows, ...row ];
            row = [];
        }
        return sudoku;
    }

    useEffect(() => {
        const sudoku = createSudokValues();
        console.log(sudoku)
        setSudokuList(sudoku);
    }, []);

    return (
        <div className="App">
            <div className="pageContainer">
                <p>Sudoku</p>
                <div className="sudokuContainer">
                    {/* 直接用state里的值渲染,不要重复调用生成函数 */}
                    {sudokuList.map((square, idx) =>
                        <Square key={idx}>{square.digit}</Square>
                    )}
                </div>
            </div>
        </div>
    );
}

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 16:06:04