如何针对React列表项实现JS对象逐项缓存以避免重复渲染?
解决React列表项全量重渲染问题
核心问题分析
你的代码已经用了useMemo包裹列表、React.memo包裹子组件,但仍出现全量重渲染,根源在于:
- 子组件
SelectorInstrumentItem的onClick回调中引用了父组件的handleDropdownItemEvent函数,而该函数每次父组件渲染都会重新创建,导致子组件的隐式依赖发生变化,React.memo的浅比较失效。 - 虽然你在状态更新时保持了未修改项的引用不变,但函数引用的不稳定会让
React.memo误判props发生变化,触发全量重渲染。
解决方案
1. 稳定事件处理函数的引用
用useCallback包裹父组件中的handleDropdownItemEvent,确保只有当依赖项变化时,函数引用才会更新:
import { useCallback } from 'react'; // 父组件内定义 const handleDropdownItemEvent = useCallback((e: any, instrumentId: string) => { e.stopPropagation(); // 你的业务逻辑代码 }, []); // 这里添加函数依赖的状态/prop,比如用到的setState或其他变量
2. 优化子组件的props传递与比较逻辑
将稳定后的函数作为props传递给子组件,而非在子组件内部创建回调;同时可自定义React.memo的比较逻辑,精准控制重渲染触发条件:
子组件代码:
import { memo } from 'react'; // 自定义比较函数(可选,用于精准判断props是否变化) const areEqual = (prevProps, nextProps) => { // 仅比较影响UI的关键属性:id和view,以及稳定的函数引用 return prevProps.selectorInstrument.id === nextProps.selectorInstrument.id && prevProps.selectorInstrument.view === nextProps.selectorInstrument.view && prevProps.handleDropdownItemEvent === nextProps.handleDropdownItemEvent; }; const SelectorInstrumentItem = memo(({ selectorInstrument, handleDropdownItemEvent }: { selectorInstrument: InstrumentSelectorInstrument, handleDropdownItemEvent: (e: any, id: string) => void }) => { return ( <StyledMenuItem view={selectorInstrument.view} sx={{ paddingX: 1.5, borderRadius: 2, }} className='flex rounded-sm' onClick={(e) => { e.stopPropagation(); handleDropdownItemEvent(e, selectorInstrument.id); }} > {/* 组件内容 */} </StyledMenuItem> ); }, areEqual);
3. 修正列表渲染的依赖项
在父组件的useMemo中添加稳定后的函数作为依赖,确保列表仅在状态或函数变化时重新生成:
const InstrumentList = useMemo(() => { return Object.values(selectorInstruments).map((selectorInstrument) => ( <SelectorInstrumentItem key={selectorInstrument.id} selectorInstrument={selectorInstrument} handleDropdownItemEvent={handleDropdownItemEvent} /> )); }, [selectorInstruments, handleDropdownItemEvent]);
4. 保持状态更新的不可变性(已正确实现,继续保持)
你当前的状态更新逻辑已经遵循了不可变性原则,未修改的项引用保持不变,这是避免不必要重渲染的基础,无需调整:
setSelectorInstruments(prevState => { const newState = {...prevState}; if (newState[instrumentId]) { newState[instrumentId] = { ...newState[instrumentId], view: instrumentInformation.view }; } if (newState[oldPrimaryInstrument.instrument.id]) { newState[oldPrimaryInstrument.instrument.id] = { ...newState[oldPrimaryInstrument.instrument.id], view: oldPrimaryInstrument.view }; } return newState; });
效果验证
完成上述修改后,只有被修改的两个列表项会触发重渲染,其余48个项会被React.memo缓存,避免不必要的渲染,解决页面卡顿问题。
内容的提问来源于stack exchange,提问作者Caden The Yak
相关产品推荐
相关产品推荐

