首页 > 解决方案 > 重用 React 元素可防止组件更新

问题描述

Foo是一个应该只渲染一次的组件。这可以用于性能优化,尽管这只是纯粹的理论问题,没有解决任何特定的编码问题。

这可以通过使用shouldComponentUpdate或纯组件来实现,这是推荐的方法:

const Foo = () => <p>{Math.random()}</p>;
const FooOnce = React.memo(Foo);
const Bar = () => {
  const [, update] = useState();

  useEffect(() => {
    setTimeout(() => {
      update({});
    }, 500);
  }, []);

  return <>
    <FooOnce/>
  </>;
};

或者通过保留对 React 元素对象的引用并重用它:

const fooOnce = <Foo/>;
const Bar = () => {
  const [, update] = useState();

  useEffect(() => {
    setTimeout(() => {
      update({});
    }, 500);
  }, []);

  return <>
    {fooOnce}
  </>;
};

重用元素对象时不会重新渲染组件是直观的,但我从自己的经验中知道这一点,而不是从官方来源。与纯组件相比,这可能会导致更少的createElement调用,因此可以认为这是一个好处。

这种行为是否在所有 React 版本中都有记录和预期?

是否有理由不以这种方式重用元素来防止组件更新?

标签: reactjs

解决方案


这种行为是否在所有 React 版本中都有记录和预期?

不,我没有找到说明这种行为的文档。

通过深入研究 React 源代码并设置一些断点,我发现通过保存和重用 createElement 结果(const fooOnce = <Foo/>;),您每次都将有效地使用完全相同的 props 对象,而在每次渲染中调用 createElement 都会创建一个新的 props 对象。

function beginWork目前,由于纤维引擎中的以下条件,这确实有所不同:if (oldProps !== newProps || hasLegacyContextChanged())

当 props 对象具有相同的标识时,react 立即认为该元素没有任何工作要做(甚至不调用render)。

这对我来说似乎很有可能继续工作,但当然不能保证这种优化将始终与这种确切的行为一致。

我指的代码在这里: https ://github.com/facebook/react/blob/c7398f33966c4fedcba2c48e915b379e8f334607/packages/react-reconciler/src/ReactFiberBeginWork.js#L2119

当您只使用正常时,您最终会调用渲染,并且由于输出不同,它将更新 DOM。

当你使用 React.memo 时,props不会有相同的标识,但随后 react 会进入 memo 组件案例(https://github.com/facebook/react/blob/c7398f33966c4fedcba2c48e915b379e8f334607/packages/react-reconciler/src/ ReactFiberBeginWork.js#L2377)并做一个 shallowEquals 比较(https://github.com/facebook/react/blob/c7398f33966c4fedcba2c48e915b379e8f334607/packages/react-reconciler/src/ReactFiberBeginWork.js#L487

是否有理由不以这种方式重用元素来防止组件更新?

这可以改变,所以我不会在生产代码中使用它。

除了这个原因,我会避免这种情况,因为组件是否更新的决定来自组件本身,因为现在要使用它,您需要更改使用者代码,而不是仅仅更改组件上的代码或向其添加包装器。


推荐阅读