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

React二维数组状态不更新问题:点击网格按钮后grid状态始终输出空数组

React二维数组状态不更新问题:点击网格按钮后grid状态始终输出空数组

嘿,这个问题我碰到过好几次了,核心原因是闭包陷阱在搞鬼,我给你拆解清楚:

问题根源

你在createGrid函数里创建单元格的onClick事件时,绑定的是当时的reveal函数。而这个reveal函数捕获的是它创建瞬间的grid状态——也就是初始的空数组[]。

为什么会这样?因为React的setState是异步操作:当你在useEffect里调用createGrid并执行setGrid([...arr])后,grid并不会立刻更新。这时候生成的按钮点击事件,引用的还是旧的reveal闭包,里面的grid始终停留在初始值。哪怕之后grid真的更新了,按钮的点击事件也不会自动拿到最新状态。

几种实用解决办法

我给你推荐三个简单有效的方案,按需选择:

方案1:用useRef保存最新的grid状态

useRef的值是可变的,而且不会触发组件重新渲染,能始终拿到最新的状态,是解决这类闭包问题的常用方案:

const App = () => {
  const [valid, setValid] = React.useState(false);
  const [grid, setGrid] = React.useState([]);
  const [input, setInput] = React.useState(null);
  // 用ref同步最新的grid状态
  const gridRef = React.useRef(grid);

  // 每次grid更新时,同步到ref里
  React.useEffect(() => {
    gridRef.current = grid;
  }, [grid]);

  const reveal = () => {
    console.log("grid is ", gridRef.current); // 从ref中获取最新值
  };

  const createGrid = () => {
    if (input !== "3") return [];

    const arr = Array.from({ length: 10 }, () =>
      Array.from({ length: 10 }, () => ({
        onClick: () => reveal(),
      }))
    );
    setGrid([...arr]);
  };

  React.useEffect(() => {
    setValid(input == "3");
    createGrid();
  }, [input]);

  return (
    <div>
      <Input setInput={setInput} />
      {valid && <Grid grid={grid} />}
    </div>
  );
};

方案2:点击时直接传递当前grid到reveal

修改Grid组件和点击事件逻辑,把当前的grid作为参数直接传入,彻底绕开闭包问题:

// 修改Grid组件,让点击事件传递当前grid
const Grid = ({ grid }) =>
  grid.flat().map((cell, index) => (
    <button key={index} onClick={() => cell.onClick(grid, index)}>
      {index}
    </button>
  ));

// 修改App里的reveal和createGrid
const reveal = (currentGrid) => {
  console.log("grid is ", currentGrid);
};

const createGrid = () => {
  if (input !== "3") return [];

  const arr = Array.from({ length: 10 }, () =>
    Array.from({ length: 10 }, () => ({
      onClick: (grid) => reveal(grid),
    }))
  );
  setGrid([...arr]);
};

方案3:用useCallback包裹reveal并依赖grid

这个方案逻辑更直观,但每次grid更新时会重新创建整个网格数组,小网格场景下完全没问题:

const App = () => {
  const [valid, setValid] = React.useState(false);
  const [grid, setGrid] = React.useState([]);
  const [input, setInput] = React.useState(null);

  // 用useCallback包裹reveal,依赖grid保证拿到最新值
  const reveal = React.useCallback(() => {
    console.log("grid is ", grid);
  }, [grid]);

  // 同样用useCallback包裹createGrid,依赖reveal确保点击事件用最新的reveal
  const createGrid = React.useCallback(() => {
    if (input !== "3") return [];

    const arr = Array.from({ length: 10 }, () =>
      Array.from({ length: 10 }, () => ({
        onClick: () => reveal(),
      }))
    );
    setGrid([...arr]);
  }, [input, reveal]);

  React.useEffect(() => {
    setValid(input == "3");
    createGrid();
  }, [input, createGrid]);

  return (
    <div>
      <Input setInput={setInput} />
      {valid && <Grid grid={grid} />}
    </div>
  );
};

总结

最推荐方案1,用useRef保存最新状态,既不会额外触发组件渲染,也能稳定拿到最新的grid值。方案2也很简洁,直接传递当前状态,彻底避免闭包陷阱。

备注:内容来源于stack exchange,提问作者K Split X

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:09:38