React开发数独应用时数组state用扩展运算符未追加数据如何解决
问题原因
- React的
setState是异步批量更新的,你在createSudokValues的for循环中连续调用setSudoku时,函数作用域内捕获的sudoku变量永远是初始的空数组,不会随之前的setSudoku调用实时更新,导致生成第2~9行时,unusedDigitInRowAndColumn判断列重复的逻辑拿到的永远是空数组,甚至会出现可选数字为空返回undefined的异常,提前终止执行。 - 你在循环中逐行调用
setSudoku的逻辑没必要,完全可以先生成完整的数独数组,再一次性赋值给state,避免异步更新带来的闭包问题。 - 内层冗余的
while循环可以直接删除,外层的列循环已经保证了每行生成9个格子。
修正方案
把逐行更新state的逻辑改为先在本地变量生成完整数独数组,再一次性调用setSudoku赋值,同时增加了可用数字为空的兜底逻辑、给监听sudoku的useEffect补充了依赖项避免不必要的执行:
import React, { useEffect, useState } from 'react'; import '../App.css'; import { SquareType } from '../interfaces/customInterfaces'; import Square from './Square'; function App() { const [sudoku, setSudoku] = 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( fullSudoku: Array<SquareType>, currentRow: Array<SquareType>, columnIndex: number ) { const digitsExistingInRow = currentRow.map(square => square.digit); const unusedDigitsInRow = possibleOptionsForDigit.filter(digit => !digitsExistingInRow.includes(digit)); const digitsExistingInColumn = fullSudoku.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)); // 兜底避免生成死局时无可用数字报错 if (unusedDigits.length === 0) return possibleOptionsForDigit[generateRandomArrayIndex(possibleOptionsForDigit)] return unusedDigits[generateRandomArrayIndex(unusedDigits)]; } function createSudokValues() { // 本地变量存储完整数独,规避state异步更新问题 const fullSudoku: SquareType[] = []; for ( let y = 1; y <= 9; y++ ) { const row: SquareType[] = []; for ( let columnIndex = 1; columnIndex <= 9; columnIndex++ ) { const generatedUnusedDigit = unusedDigitInRowAndColumn(fullSudoku, row, columnIndex); row.push( { digit: generatedUnusedDigit, index: columnIndex, shown: true } ); } fullSudoku.push(...row); } // 生成完成后一次性更新state setSudoku(fullSudoku); } useEffect(() => { console.log('sudoku ', sudoku) }, [sudoku]); useEffect(() => { createSudokValues(); }, []); return ( <div className="App"> <div className="pageContainer"> <p>Sudoku</p> <div className="sudokuContainer"> { sudoku.map((square, idx) => <Square key={idx} {...square}></Square> ) } </div> </div> </div> ); } export default App;
内容的提问来源于stack exchange,提问作者rumon
相关产品推荐
相关产品推荐

