基本使用

React Memo是函数式组件中通过包裹组件 来达到优化render行为的。
基本使用:

  1. const ComponentA = React.Memo((props) => {});
  2. 或者
  3. export default React.memo(ComponentA);

当ComponentA的父组件状态发生改变的时候,只有当props变化的时候,才会触发ComponentA的re-render。
类似于类组件的PureComponent.

  1. React.Memo(C,(preProps,nextzprops)=>{
  2. // 做我们想做的事情,类似shouldComponentUpdate
  3. // 返回 true or false
  4. })

Memo接收第二个参数,回调函数。
当函数return true的时候,告诉了react这两个props是一样的,不用重新执行整个函数组件;反之false的时候会重新执行该组件

我们以任务列表为例,来看看React Memo是如何做到优化的
代码在线运行:
https://stackblitz.com/edit/react-unfgme

  1. import React from "react";
  2. import { v4 as uuidv4 } from "uuid";
  3. const List = ({ list }) => {
  4. console.log("Render: List");
  5. return (
  6. <ul>
  7. {list.map(item => (
  8. <ListItem key={item.id} item={item} />
  9. ))}
  10. </ul>
  11. );
  12. };
  13. const ListItem = ({ item }) => {
  14. console.log("Render: ListItem");
  15. return <li>{item.name}</li>;
  16. };
  17. const App = () => {
  18. console.log("Render: App");
  19. const [users, setUsers] = React.useState([
  20. { id: "a", name: "Robin" },
  21. { id: "b", name: "Dennis" }
  22. ]);
  23. const [text, setText] = React.useState("");
  24. const handleText = event => {
  25. setText(event.target.value);
  26. };
  27. const handleAddUser = () => {
  28. setUsers(users.concat({ id: uuidv4(), name: text }));
  29. };
  30. return (
  31. <div>
  32. <input type="text" value={text} onChange={handleText} />
  33. <button type="button" onClick={handleAddUser}>
  34. Add User
  35. </button>
  36. <List list={users} />
  37. </div>
  38. );
  39. };
  40. export default App;

大概分别App、List、ListItem组件,分别console,发现,每次input中输入值的时候,都会触发子组件re-render。
当应用越来越大的时候,会严重影响性能。

  1. const List = React.memo(({ list }) => {
  2. console.log('Render: List');
  3. return (
  4. <ul>
  5. {list.map((item) => (
  6. <ListItem key={item.id} item={item} />
  7. ))}
  8. </ul>
  9. );
  10. });

当我们再次输入的时候,会发现,只会打印App
image.png
然而,当我们点击 Add User的时候
image.png
List中的props list改变了,ListItem重新渲染了,并且渲染了3次。
但是往往我们仅仅需要render新增的那次渲染就好了

  1. const ListItem = React.memo(({ item }) => {
  2. console.log('Render: ListItem');
  3. return <li>{item.name}</li>;
  4. });

同样的,我们用memo包裹ListItem组件,只有当item变化的时候,子组件ListItem才会重新渲染
image.png

你可能疑惑,既然React Memo能够优化,为什么React memo不为React Component设置默认被包裹呢?
在React Memo内部会对比前后的props的变化来决定是否re-render,通常这种计算和re-render来对比是更expensive。

总之,当应用开始变得很慢,React’s memo 派上用场!
Often this happens in data heavy components, like huge lists where lots of components have to rerender once a single data point changes.

我们可以使用 memo 包一层,就能解决上面的问题;但是仅仅解决父组件没有传参给子组件的情况以及父组件传简单类型的参数给子组件的情况(例如 string、number、boolean等);

如果有传复杂属性应该使用 useCallback(回调事件)
useMemo(复杂属性)

Reference

https://www.robinwieruch.de/react-memo
https://mp.weixin.qq.com/s/fF-H3Lr0aP3Ld8jfJrrwQg