四个导致组件 re-render 的原因:状态变化父组件 re-renderContext 变化Hooks 变化

如何排查组件是否 re-render?

// 组件层加一级 Math.random() 输出
<div>{ Math.random()  }</div>

Props 变化会导致 re-render?

不会,props 往上可以追溯到 state 变更,是 state 变更导致父组件 re-render 从而引发子组件 re-render,而不是由 props 变更引起,除非你有用 React.memo

如何避免父组件 re-render 导致的 re-render?

React.memo。使用 React.memo 后的组件只有 props 变更才会触发 re-render。

为什么这不是默认行为?因为作为开发者,我们往往高估了重新渲染的成本。对于 props 很多且没有很多子组件的组件来说,相比 re-render,检查 props 是否变更带来的消耗可能更大。因此,如果对每个组件都进行 React.memo,可能会产生反效果。

Dan 在「Before You memo()」一文中提到,在使用 React.memo() 之前,还可以考虑两个方法,让 re-render 保持在一个很小的范围之内。

  1. 把状态往下移,把可变的部分拆到平行组件里,比如 <Changed /><Expansive />,确保更新只在 <Changed /> 组件里;
  2. 把内容往上提,把可变的部分拆到父级组件里,比如 <Changed><Expansive /></Changed>,在 <Changed /> 变更时只要 props.children 不变化,就不会触发子组件的 re-render。(用了 props.children,而 props 不会触发 re-render。那用其他 props 属性可以吗?可以!比如 <Changed left={<Expansive1 />} right={<Expansive2 />} /><Changed /> re-render 并不会导致 <Expansive /> re-render。这种方法叫「componets as props」)

什么时候应该用 useMemo / useCallback?

  1. React.memo 过的组件的 props 应该用,因为他们只有 props 变更时才会 re-render,所以反之非 React.memo 过的组件的 props 无需使用,因为都会 re-render;
  2. useEffectuseMemouseCallback 中非原始值的依赖应该用;
  3. 重消耗(比如生成渲染树)的部分应该用,反之轻消耗不要用,因为 useMemo/useCallback 本身也有消耗;
  4. JavaScript 中有原始值和引用值的区分,由于 propshooks dep 都会做 shadow equal,使用时尽量避免使用引用值,避免不了时需用 useMemo/useCallback 包一下。

避免使用引用值有几个方法

  1. 拍平,比如 options={{ showSideBar: true }} 改成 showSideBar=true