函数组件中的 shouldComponentUpdate

新手上路,请多包涵

我对 React 的 shouldComponentUpdate (未覆盖时)有疑问。我确实更喜欢纯函数组件,但我担心它每次都会更新,即使 props/state 没有改变。所以我正在考虑改用 PureComponent 类。

我的问题是:功能组件是否具有相同的 shouldComponentUpdate 检查为 PureComponents?还是每次都更新?

原文由 MoeSattler 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 645
2 个回答

在 React 中,功能组件是无状态的,它们没有生命周期方法。 无状态组件是编写 React 组件的一种优雅方式,在我们的包中不需要太多代码。 但是在内部,无状态组件被包装在一个类中,当前没有应用任何优化。这意味着无状态和有状态组件在内部具有相同的代码路径(尽管我们对它们的定义不同)。

但在未来 React 可能会优化无状态组件,如下所述:

将来,我们还可以通过避免不必要的检查和内存分配来针对这些组件进行性能优化。 [更多阅读…]

shouldComponentUpdate

这是我们可以应用我们的自定义优化并避免不必要的重新渲染组件的地方。该方法与不同类型的组件的用法说明如下:

  • 功能性无状态组件

如前所述,无状态组件没有生命周期方法,因此我们无法使用 shouldComponentUpdate 优化它们。但是它们已经以不同的方式进行了优化,它们具有更简单和优雅的代码结构,并且比具有所有生命周期钩子的组件花费更少的字节。

  • 扩展 React.PureComponent

React v15.3.0 开始,我们有一个名为 PureComponent PureRenderMixin 扩展。在引擎盖下,这采用了 shouldComponentUpdate 中当前道具/状态与下一个道具/状态的浅比较。

也就是说,我们仍然不能依赖 PureComponent 类将我们的组件优化到我们想要的水平。如果我们有 Object 类型(数组、日期、普通对象)的道具,就会发生这种异常情况。这是因为我们在比较对象时遇到了这个问题:

   const obj1 = { id: 1 };
  const obj2 = { id: 1 };
  console.log(obj1 === obj2); // prints false

因此,肤浅的比较不足以确定事情是否发生了变化。但是,如果您的道具只是字符串、数字、布尔值……而不是对象,请使用 PureComponent 类。如果您不想实现自己的自定义优化,也可以使用它。

  • 扩展 React.Component

考虑上面的例子;如果我们知道如果 id 发生了变化,那么我们可以通过比较 obj1.id === obj2.id 来实现我们自己的自定义优化。那就是我们可以 extend 我们正常的 Component 基类并使用 shouldComponentUpdate 自己做特定键的比较。

原文由 yadhu 发布,翻译遵循 CC BY-SA 3.0 许可协议

功能组件将在每次父级渲染时重新渲染,无论 props 是否已更改。

但是,使用 React.memo 高阶组件,功能组件实际上有可能获得相同的 shouldComponentUpdate 检查哪个用于 PureComponent https://react– 。 org/docs/react-api.html#reactmemo

您可以简单地将您的功能组件包装在 React.memo 导出时如此处所示。

所以

const SomeComponent = (props) => (<div>HI</div>)
export default SomeComponent

可以改为

const SomeComponent = (props) => (<div>HI</div>)
export default React.memo(SomeComponent)

例子

以下示例显示这如何影响重新渲染

父组件只是一个常规的功能组件。它使用新的 反应钩子 来处理一些状态更新。

它只有一些 tick 状态,仅用于提供一些关于我们重新渲染道具的频率的线索,同时它强制每秒重新渲染父组件两次。

此外,我们有一个 clicks 状态,它告诉我们点击按钮的频率。这就是我们送给孩子们的道具。 因此,如果我们使用 React.memo ,他们应该只在点击次数发生变化时重新渲染

现在请注意,我们有两个不同类型的孩子。一个包裹在 memo 中,一个不是。 Child 未包装,每次父级重新渲染时都会重新渲染。 MemoChild 已包装,只有在点击属性发生变化时才会重新呈现。

 const Parent = ( props ) => {
  // Ticks is just some state we update 2 times every second to force a parent rerender
  const [ticks, setTicks] = React.useState(0);
  setTimeout(() => setTicks(ticks + 1), 500);
  // The ref allow us to pass down the updated tick without changing the prop (and forcing a rerender)
  const tickRef = React.useRef();
  tickRef.current = ticks;

  // This is the prop children are interested in
  const [clicks, setClicks] = React.useState(0);

  return (
    <div>
      <h2>Parent Rendered at tick {tickRef.current} with clicks {clicks}.</h2>
      <button
        onClick={() => setClicks(clicks + 1)}>
        Add extra click
      </button>
      <Child tickRef={tickRef} clicks={clicks}/>
      <MemoChild tickRef={tickRef} clicks={clicks}/>
    </div>
  );
};

const Child = ({ tickRef, clicks }) => (
  <p>Child Rendered at tick {tickRef.current} with clicks {clicks}.</p>
);

const MemoChild = React.memo(Child);

现场示例(也在 CodePen 上):

 console.log("HI");

const Parent = ( props ) => {
  const [ticks, setTicks] = React.useState(0);
  const tickRef = React.useRef();
  tickRef.current = ticks;
  const [clicks, setClicks] = React.useState(0);

  setTimeout(() => setTicks(ticks + 1), 500);
  return (
    <div>
      <h2>Parent Rendered at tick {tickRef.current} with clicks {clicks}.</h2>
      <button
        onClick={() => setClicks(clicks + 1)}>
        Add extra click
      </button>
      <Child tickRef={tickRef} clicks={clicks}/>
      <MemoChild tickRef={tickRef} clicks={clicks}/>
    </div>
  );
};

const Child = ({ tickRef, clicks }) => (
  <p>Child Rendered at tick {tickRef.current} with clicks {clicks}.</p>
);

const MemoChild = React.memo(Child);

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
 <div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

原文由 the_cheff 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进