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

切换React移动端/桌面端布局时,如何阻止子组件重复渲染?

问题

我有移动端和桌面端两种布局,结构不同但都会注入相同的子组件。希望在布局切换时阻止子组件重渲染,尝试过React.memo包裹子组件、在Parent组件内使用useMemo都无效。复现代码如下:

import React, { useState } from "react";

interface ParentProps {
  children: React.ReactNode;
}

const Parent = ({ children }: ParentProps): JSX.Element => {
  const [value, setValue] = useState(false);

  if (value) {
    return (
      <>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into false
        </button>
        {children}
      </>
    );
  }
  return (
    // 桌面端额外元素
    <div> 
      <div>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into true
        </button>
        {children}
      </div>
    </div>
  );
};

const ChildrenComponent = () => {
  console.log("rerender Children...");
  return <p>children component</p>;
};

export const App = ()=> {
  return (
    <Parent>
      <ChildrenComponent />
    </Parent>
  );
}

原因分析

你遇到的不是单纯的重渲染问题,而是布局切换时React会卸载旧的子组件并重新挂载:当Parent的state变化,返回的JSX结构完全不同(一个是<>包裹,一个是嵌套<div>),React的Diff算法会认为这是两棵完全不同的树,因此会销毁原有子组件实例,重新创建新的实例。这时候React.memo和useMemo都起不到作用——因为它们只能阻止组件在props不变时的重渲染,无法阻止卸载再挂载。

解决方案

方法1:给子组件容器添加稳定的key,配合React.memo

让React识别出两个布局分支里的子组件容器是同一个元素,避免卸载再挂载,同时用React.memo阻止子组件不必要的重渲染:

  1. 用React.memo包裹子组件:
const ChildrenComponent = React.memo(() => {
  console.log("rerender Children...");
  return <p>children component</p>;
});
  1. 修改Parent组件,在两个布局分支里给子组件包裹同一个带稳定key的容器:
const Parent = ({ children }: ParentProps): JSX.Element => {
  const [value, setValue] = useState(false);

  if (value) {
    return (
      <>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into false
        </button>
        {/* 统一容器key */}
        <div key="child-wrapper">{children}</div>
      </>
    );
  }
  return (
    <div> 
      <div>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into true
        </button>
        {/* 统一容器key */}
        <div key="child-wrapper">{children}</div>
      </div>
    </div>
  );
};

方法2:用useMemo保持子组件引用稳定(可选)

如果需要进一步确保children的引用不随Parent重渲染变化,可以在Parent中用useMemo缓存children:

const Parent = ({ children }: ParentProps): JSX.Element => {
  const [value, setValue] = useState(false);
  // 缓存children,避免Parent重渲染时重新创建引用
  const stableChildren = useMemo(() => children, []);

  if (value) {
    return (
      <>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into false
        </button>
        <div key="child-wrapper">{stableChildren}</div>
      </>
    );
  }
  return (
    <div> 
      <div>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into true
        </button>
        <div key="child-wrapper">{stableChildren}</div>
      </div>
    </div>
  );
};

方法3:使用React.Children处理单个子组件(适用于明确只有一个子组件的场景)

如果确定Parent只接收一个子组件,可以用React.Children.only确保引用稳定,结合React.memo:

const Parent = ({ children }: ParentProps): JSX.Element => {
  const [value, setValue] = useState(false);
  const onlyChild = React.Children.only(children);

  if (value) {
    return (
      <>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into false
        </button>
        <div key="child-wrapper">{onlyChild}</div>
      </>
    );
  }
  return (
    <div> 
      <div>
        <button type="button" onClick={() => setValue((val) => !val)}>
          Turn into true
        </button>
        <div key="child-wrapper">{onlyChild}</div>
      </div>
    </div>
  );
};

验证

修改后点击切换按钮,子组件的console.log不会再触发,说明既没有重渲染也没有被卸载再挂载。

内容的提问来源于stack exchange,提问作者Purpurovv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 02:27:46