React嵌套数组更新后组件不重渲染(数独开发场景)
问题分析与解决方案
核心问题
你的组件没更新内容,最关键的原因是渲染棋盘时用的是初始的blankSquares,而非状态里的board。不管setBoard怎么更新board,页面始终渲染的是一开始定义的固定数组,自然看不到变化。
另外还有个潜在坑:初始化blankSquares时,Array(9).fill(cube)会让数组每一项都引用同一个cube数组,导致修改某一行时所有行都会联动,虽然你在setCell里做了拷贝,但初始状态的这个问题得先修复。
分步解决
1. 修复棋盘渲染的数据源
把渲染逻辑里的blankSquares.map改成board.map,这样才会跟着状态变化重新渲染:
const bigBoard = board.map((item, index1) => { const sudokuRow = item.map((cell, index2) => { return <td className={`_${index1}-${index2}`} key={`${index1}-${index2}`} onClick={(e) => setChosen(e.target.className)}>{cell}</td> }) return <tbody className={`square${index1}`} key={index1}><tr key={index1}>{sudokuRow}</tr></tbody> })
注:这里的shortid.generate()完全没必要,反而会每次渲染生成新ID浪费性能,直接用索引当key就行,毕竟数独棋盘是固定的9x9结构。
2. 修复初始数组的引用问题
原来的blankSquares创建方式会导致所有行共享同一个数组,改成用Array.from生成独立的行数组:
// 替换原来的cube和blankSquares定义 const blankSquares = Array.from({length: 9}, () => Array(9).fill('X'));
这样每一行都是独立的数组,避免后续修改时出现意外的联动。
3. 简化setCell逻辑(可选优化)
你的setCell拷贝逻辑是对的,可以简化写法同时确保坐标解析正确:
function setCell(val) { if (!chosen) return; const [, outerStr, innerStr] = chosen.split('-'); const outer = Number(outerStr); const inner = Number(innerStr); // 逐层拷贝比JSON序列化更高效 const newBoard = board.map(row => [...row]); newBoard[outer][inner] = val; setBoard(newBoard); }
完整修正后的代码片段
import { useState } from 'react'; export default function NineSquare() { // 初始化独立的9x9空棋盘 const blankSquares = Array.from({length: 9}, () => Array(9).fill('X')); const [board, setBoard] = useState(blankSquares); const [chosen, setChosen] = useState(); function setCell(val) { if (!chosen) return; const [, outerStr, innerStr] = chosen.split('-'); const outer = Number(outerStr); const inner = Number(innerStr); const newBoard = board.map(row => [...row]); newBoard[outer][inner] = val; setBoard(newBoard); } // 基于board状态渲染棋盘 const bigBoard = board.map((item, index1) => { const sudokuRow = item.map((cell, index2) => { return ( <td className={`_${index1}-${index2}`} key={`${index1}-${index2}`} onClick={(e) => setChosen(e.target.className)} > {cell} </td> ) }) return ( <tbody className={`square${index1}`} key={index1}> <tr key={index1}>{sudokuRow}</tr> </tbody> ) }) return ( <> <table className="board">{bigBoard}</table> <Numbers setCell={setCell}/> </> ) }
内容的提问来源于stack exchange,提问作者fatjojo
相关产品推荐
相关产品推荐

