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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:59:50