React中大型二维状态数组引发卡顿的优化方案咨询
React大型二维数组绘图应用性能优化方案
针对大型二维数组导致React应用卡顿的问题,可通过以下核心方向优化:
1. 修复初始状态的引用共享问题
原代码中Array(width).fill(Array(length).fill(false))会让所有列共享同一个数组实例,不仅会导致状态更新异常,还会引发不必要的组件重渲染。修改为:
import { useState, useCallback, memo } from "react"; function App() { const width = 200; const length = 200; // 每列都是独立数组,避免引用共享 const [selected, setSelected] = useState(() => Array.from({ length: width }, () => Array(length).fill(false)) ); // ... }
2. 优化状态更新逻辑
原更新方式会创建整列的新数组,内存开销大。改为仅复制并修改目标元素,同时用useCallback缓存事件处理函数,避免每次渲染创建新函数:
const columnSquareSelected = useCallback((columnInd, squareInd) => { setSelected(prev => { // 仅复制需要修改的列 const newColumn = [...prev[columnInd]]; newColumn[squareInd] = true; // 仅复制外层数组的必要部分 return [...prev.slice(0, columnInd), newColumn, ...prev.slice(columnInd + 1)]; }); }, []);
3. 阻止不必要的组件重渲染
用React.memo包裹Square和Column组件,只有当props真正变化时才触发重渲染:
const Square = memo(({ squareVal, setSquareVal }) => { return ( <button className={`h-1 w-1 ${squareVal ? "bg-red-600" : "bg-gray-300"}`} onMouseEnter={setSquareVal} ></button> ); }); const Column = memo(({ columnArr, columnInd, columnSquareSelected }) => { return ( <div className="flex flex-col"> {columnArr.map((sqrVal, sqrInd) => ( <Square key={`${columnInd}-${sqrInd}`} squareVal={sqrVal} setSquareVal={() => columnSquareSelected(columnInd, sqrInd)} /> ))} </div> ); });
4. 引入虚拟滚动(核心优化)
当元素数量达到40000个时,过多DOM节点是卡顿的主要原因。使用虚拟滚动只渲染视口内的元素,以react-window为例:
首先安装依赖:
npm install react-window
然后修改App组件的渲染逻辑:
import { FixedSizeList as List } from 'react-window'; function App() { // ... 状态和事件处理函数不变 // 渲染单列的回调 const renderColumn = ({ index, style }) => { return ( <div style={style}> <Column columnArr={selected[index]} columnInd={index} columnSquareSelected={columnSquareSelected} /> </div> ); }; return ( <> <div> <h1 className="text-5xl large font-semibold">Heart Rate Faker</h1> <h2 className="text-xl font-semibold">Create fake heart rate data</h2> </div> <div className="flex justify-center items-center"> <List height={length} // 列总高度(每个Square占1px) width={width} // 总宽度(每个Column占1px) itemCount={width} // 列的数量 itemSize={1} // 每列宽度 > {renderColumn} </List> </div> </> ); }
5. 细节优化
- 简化类名拼接:直接用模板字符串代替
classNames函数,减少函数调用开销; - 优化key值:用
${columnInd}-${sqrInd}代替空格拼接,避免潜在的key冲突。
内容的提问来源于stack exchange,提问作者Alan Ganser
相关产品推荐
相关产品推荐

