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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:52:59