如何解决memo()无法阻止React组件因editMode变更重渲染的问题
问题描述
我有一个循环渲染不同子组件的map结构,已为map内的每个组件添加export memo(function componentName()...),但当editMode状态变更时,组件仍会重复渲染。我还尝试对data状态使用useMemo/useCallback,但问题依旧,请问我遗漏了什么要点?
状态代码
const {data, setData} = useState(false) const [editMode, setEditMode] = useState(false)
Map代码
{ data.dataWidget.length > 0 && data.dataWidget.map((item) => { return ( <div key={item.id} data-grid={{ x: item.x, y: item.y, w: item.w, h: item.h, isResizable: editMode, isDraggable: editMode, resizeHandles: ["se", "sw", "ne", "nw", "e", "w", "n", "s"], }} > {item.analytic_model.type != "indicator" ? ( <div className={`${"border border-neutral-40 h-full flex flex-col"}`} > <div className="p-16 flex justify-between border-b border-neutral-40"> <Header setData={setData} analytic_widget={item} isModalVisible={() => { setDataConfigModal((prev) => { return { ...prev, isVisible: !prev.isVisible, data: item, }; }); }} /> </div> {item.analytic_model.type == "canvajs_graph" && ( <div className={`p-16 h-full canvas_js_${item.id}`}> <CanvaJsGraph analytic_widget={item} dataDashboard={data.dataDashboard} /> </div> )} {item.analytic_model.type == "chartjs_graph" && ( <div className="p-16 h-full"> <ChartJsGraph analytic_widget={item} dataDashboard={data.dataDashboard} /> </div> )} {item.analytic_model.type == "chartjs_bar" && ( <> <div className="p-16 h-full"> <ChartJsBar analytic_widget={item} dataDashboard={data.dataDashboard} /> </div> </> )} {item.analytic_model.type == "leaflet_position" && ( <div className="p-16 h-full"> <LeafletPosition analytic_widget={item} dataDashboard={data.dataDashboard} /> </div> )} {item.analytic_model.type == "leaflet_polyline" && ( <div className="p-16 h-full"> <LeafletPolyline analytic_widget={item} dataDashboard={data.dataDashboard} /> </div> )} {item.analytic_model.type == "datatable" && ( <div className="p-16 h-full overflow-y-auto"> <DatatableDashboard analytic_widget={item} dataDashboard={data.dataDashboard} /> </div> )} </div> ) : ( <Indicator analytic_widget={item} dataDashboard={data.dataDashboard} dashboardHeader={ <Header setData={setData} analytic_widget={item} isModalVisible={() => { const element = document.getElementById( `dashboard-${item.id}`, ); element.style.display = "block"; }} /> } /> )} </div> ); }); }
原因分析与解决方案
核心问题是父组件每次渲染时会创建大量新的引用类型props(对象、函数、React元素),导致子组件的memo浅比较失效,触发不必要的重渲染。以下是具体遗漏点和修复方案:
1. 内联对象props导致外层div重渲染
data-grid的配置对象是每次渲染时新建的,即使内容不变,引用也会更新,导致包裹的div重渲染,进而触发内部所有子组件重渲染。
修复:用useMemo缓存该对象
// 在map循环内部(或抽离为单独缓存逻辑) const gridConfig = useMemo(() => ({ x: item.x, y: item.y, w: item.w, h: item.h, isResizable: editMode, isDraggable: editMode, resizeHandles: ["se", "sw", "ne", "nw", "e", "w", "n", "s"], }), [item.x, item.y, item.w, item.h, editMode]); // 替换原div的data-grid属性 <div key={item.id} data-grid={gridConfig}>
2. 内联箭头函数props导致Header组件重渲染
传给Header的isModalVisible是每次渲染时新建的箭头函数,引用不同,导致memo包裹的Header组件依然重渲染。
修复:用useCallback缓存该函数
// 非indicator分支的Header回调 const handleModalOpen = useCallback(() => { setDataConfigModal((prev) => ({ ...prev, isVisible: !prev.isVisible, data: item, })); }, [setDataConfigModal, item]); // indicator分支的Header回调 const handleIndicatorModalOpen = useCallback(() => { const element = document.getElementById(`dashboard-${item.id}`); element.style.display = "block"; }, [item.id]); // 传给Header组件 <Header setData={setData} analytic_widget={item} isModalVisible={handleModalOpen} />
3. 内联React元素props导致Indicator组件重渲染
传给Indicator的dashboardHeader是每次渲染时新建的<Header>元素,React会认为这是新props,触发Indicator重渲染。
修复:用useMemo缓存该React元素
const indicatorHeader = useMemo(() => ( <Header setData={setData} analytic_widget={item} isModalVisible={handleIndicatorModalOpen} /> ), [setData, item, handleIndicatorModalOpen]); // 传给Indicator组件 <Indicator analytic_widget={item} dataDashboard={stableDataDashboard} dashboardHeader={indicatorHeader} />
4. data.dataDashboard引用不稳定
如果调用setData时每次都创建新对象,会导致data.dataDashboard引用变化,传给子组件的props更新,触发重渲染。
修复:缓存data.dataDashboard的引用
// 在组件顶部缓存 const stableDataDashboard = useMemo(() => data.dataDashboard, [data.dataDashboard]); // 子组件中使用稳定引用 <CanvaJsGraph analytic_widget={item} dataDashboard={stableDataDashboard} />
额外注意点
memo默认是浅比较,如果子组件接收复杂对象/数组类型的props,必须保证引用稳定,否则memo不生效。- 检查
setData的调用逻辑,避免不必要的状态更新(比如editMode变化时,不要意外修改data的引用)。
内容的提问来源于stack exchange,提问作者ioiofadhil
相关产品推荐
相关产品推荐

