React开发数独应用时useEffect内生成函数被调用两次如何解决
问题原因
- 核心逻辑问题:你将
row、previuosRows、sudoku三个变量定义在了App组件的顶层作用域,而不是createSudokValues函数内部,同时函数执行时没有重置这三个变量的值。多次调用createSudokValues时,会在上一次生成的数组基础上继续追加元素,导致最终数组长度超过预期的81。 - 重复调用问题:你当前代码里一共会调用两次
createSudokValues:第一次是在空依赖的useEffect回调中,第二次是在JSX渲染时的map逻辑前。如果你使用的是React18+且项目开启了严格模式(默认创建的项目都会开启),开发环境下useEffect会额外多执行一次,进一步增加调用次数。 - 冗余逻辑问题:内层循环中
while (row.length <=9)的判断完全多余,每次内层循环只会执行一次push后就break,完全可以直接去掉while语句。
解决方法
- 将三个临时变量的声明移动到
createSudokValues函数内部,确保每次调用函数都会重新初始化变量,不会复用上次的结果。 - 用
useState存储生成的数独数组,仅在组件初始化时生成一次,避免重复调用生成函数。 - 删掉冗余的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
相关产品推荐
相关产品推荐

