useMemo?你可能并不需要它

60 阅读1分钟

对这个知识点的深入是从下面这篇文章的一个小疑问开始的: 如果不使用useMemo,我们应该怎么办?

请删掉99%的useMemo

image.png

如果不使用useMemo,我们应该怎么办

下面ExpensiveTree是一个渲染极其昂贵的组件,状态color的变化会导致App的重渲染,从而导致ExpensiveTree的重新渲染

import { useState } from 'react';
 
export default function App() {
  let [color, setColor] = useState('red');
  return (
    <div>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p style={{ color }}>Hello, world!</p>
      <ExpensiveTree />
    </div>
  );
}
 
function ExpensiveTree() {
  let now = performance.now();
  while (performance.now() - now < 100) {
    // Artificial delay -- do nothing for 100ms
  }
  return <p>I am a very slow component tree.</p>;
}

那有什么办法避免呢

状态下移

将变化的部分与不变化的部分分开,此时状态colorApp组件下移到Form组件中,则color的变化只会导致Form组件的re-render。

export default function App() {
  return (
    <>
      <Form />
      <ExpensiveTree />
    </>
  );
}
 
function Form() {
  let [color, setColor] = useState('red');
  return (
    <>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p style={{ color }}>Hello, world!</p>
    </>
  );
}

内容提升

如果是最外层的div需要状态color(如下),怎么办呢

export default function App() {
  let [color, setColor] = useState('red');
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p>Hello, world!</p>
      <ExpensiveTree />
    </div>
  );
}

此时就要去了解parentowner这两个概念的区别了

有两种原因会导致组件的re-render:

  1. self-update: 自身state的变化,会导致自身的re-render
  2. owner-update:默认来说,当一个组件re-render,会导致其own的所有组件re-render。

parent和owner的区别:

  1. Parent: the component or element in which the other component is nested.
  2. Owner: the component which renders the other component.

如下代码所示,AppExpensiveTreeowner componentColorPickerExpensiveTreeparent component。故由于ExpensiveTree的owner是App,故ColorPicker的re-render不会导致ExpensiveTree的re-render。

export default function App() {
  return (
    <ColorPicker>
      <p>Hello, world!</p>
      <ExpensiveTree />
    </ColorPicker>
  );
}
 
function ColorPicker({ children }) {
  let [color, setColor] = useState("red");
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      {children}
    </div>
  );
}

参考

parents-owners-data-flow

parents-owners-performance