Redux store变更时如何避免整个表格组件重新渲染?
表格重渲染优化方案
以下是可落地的几个优化方向,按优先级从高到低排列:
- 拆分独立行组件 +
React.memo包裹
把表格行逻辑单独抽成独立组件,用React.memo包裹后默认会对组件props做浅比较,只要行对应的数据没有变化就不会触发重渲染。注意行的key必须用行数据的唯一业务ID,不能用数组下标,否则数组元素顺序变化时React会认错组件,反而触发不必要的重渲染。 - 优化Redux数据读取逻辑
不要在表格父组件一次性取出整个数组再遍历传给子组件,改为每个行组件单独通过useSelector取自身对应ID的数据:useSelector本身会做浅比较,只有当前行的实际数据发生变化时,才会触发当前行重渲染。父组件只需取出所有行的ID数组遍历渲染行组件即可,只有发生增删操作时ID数组才会变化,其他场景下父组件不会重渲染。
如果需要缓存选择器逻辑,可以搭配reselect库创建带缓存的选择器,进一步降低重复计算的开销。 - 稳定回调函数引用
不要在父组件遍历行的时候给行组件传递匿名函数,比如onClick={() => handleDelete(row.id)}这种写法每次父组件渲染都会生成新的函数实例,会击穿React.memo的浅比较逻辑。可以用useCallback包裹处理函数,保证回调引用稳定,行组件只接收行ID作为参数即可。 - 临时删除标记优化
不需要在用户点击删除时第一时间就修改Redux中的数组,可给行数据增加isDeleted临时标记:用户点击删除时仅将对应行的isDeleted标记设为true,界面上隐藏该行,此时只会触发当前行的重渲染;等撤销倒计时结束后再真正把数据从Redux数组中移除,撤销操作仅需要把对应行的isDeleted标记改回false即可,同样只会触发单行李重渲染。
该方案除了降低重渲染开销外,还能避免删除到生效的窗口期内,Redux数组被其他业务逻辑依赖引发的异常,撤销操作的响应速度也会更快。
示例代码参考:
// 独立行组件 const TableRow = React.memo(({ rowId, onDelete }) => { // 仅订阅当前行的数据,只有该行数据变化才会重渲染 const rowData = useSelector(state => state.tableList.find(item => item.id === rowId)); if (!rowData || rowData.isDeleted) return null; return ( <tr> <td>{rowData.content}</td> <td><button onClick={() => onDelete(rowId)}>删除</button></td> </tr> ) }) // 表格父组件 const Table = () => { // 仅订阅行ID数组,只有增删操作才会触发父组件重渲染 const rowIds = useSelector(state => state.tableList.map(item => item.id)); const handleDelete = useCallback((rowId) => { // 删除逻辑,仅修改对应行的isDeleted标记 }, []); return ( <table> <tbody> {rowIds.map(id => <TableRow key={id} rowId={id} onDelete={handleDelete} />)} </tbody> </table> ) }
如果你要渲染的行数量超过1000行,还可以搭配虚拟滚动方案,只渲染可视区域内的行,不管数组怎么变更,同时渲染的行数量固定,重渲染开销会进一步降低。
内容的提问来源于stack exchange,提问作者blankface
相关产品推荐
相关产品推荐

