How to use React memo【译】

原文链接www.robinwieruch.de/react-memo

React 的备忘录 API 可用于优化 React 函数组件的渲染行为。 我们先通过一个示例组件来说明问题,然后通过 React 的 memo API 来解决。

请记住,React 中的大多数性能优化都为时过早。 默认情况下,React 速度很快,因此每项性能优化都是可选的,以防某些事情开始变得缓慢。

注意:如果您的 React 组件仍在使用 React memo 进行渲染,请查看有关 React 的 useCallback Hook 的指南。 通常,重新渲染与回调处理程序相关联,每次渲染都会更改。

注意:不要将 React 的 memo API 与 React 的 useMemo Hook 混淆。 React memo 用于包装 React 组件以防止重新渲染,而 useMemo 用于记忆值。

让我们看下面的 React 应用程序示例,它呈现用户项目列表并允许我们将用户添加到列表中。 我们使用 React 的 useState Hook 使这个列表有状态:

import React from 'react';
import { v4 as uuidv4 } from 'uuid';
 
const App = () => {
  const [users, setUsers] = React.useState([
    { id: 'a', name: 'Robin' },
    { id: 'b', name: 'Dennis' },
  ]);
 
  const [text, setText] = React.useState('');
 
  const handleText = (event) => {
    setText(event.target.value);
  };
 
  const handleAddUser = () => {
    setUsers(users.concat({ id: uuidv4(), name: text }));
  };
 
  return (
    <div>
      <input type="text" value={text} onChange={handleText} />
      <button type="button" onClick={handleAddUser}>
        Add User
      </button>
 
      <List list={users} />
    </div>
  );
};
 
const List = ({ list }) => {
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
};
 
const ListItem = ({ item }) => {
  return <li>{item.name}</li>;
};
 
export default App;
复制代码

如果在 App、List 和 ListItem 组件的函数体中包含 console.log 语句,您将看到每次有人在输入字段中键入时,这些日志语句都会运行:

const App = () => {
  console.log('Render: App');
 
  ...
};
 
const List = ({ list }) => {
  console.log('Render: List');
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
};
 
const ListItem = ({ item }) => {
  console.log('Render: ListItem');
  return <li>{item.name}</li>;
};
复制代码

在输入字段中输入后,所有组件都会重新渲染,因为 App 组件会更新其状态,并且默认情况下其所有子组件都会重新渲染。

// after typing one character into the input field
 
Render: App
Render: List
Render: ListItem
Render: ListItem
复制代码

这是 React 给出的默认行为,大多数情况下,只要您的应用程序不会开始感觉缓慢,就可以保持这种状态。

但是一旦开始感觉很慢,比如每次用户在输入字段中输入时都会渲染一个巨大的项目列表,你可以使用 React 的memo API 来记住你的组件的功能:

const List = React.memo(({ list }) => {
  console.log('Render: List');
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
});
 
const ListItem = ({ item }) => {
  console.log('Render: ListItem');
  return <li>{item.name}</li>;
};
复制代码

现在,当我们在输入字段中输入时,只有 App 组件会重新渲染,因为它是唯一受更改状态影响的组件。 List 组件从之前接收它的memo props,这些prop没有改变,因此根本不会重新渲染。 ListItem 也没有使用 React 的备忘录 API,因为 List 组件已经阻止了重新渲染。

// after typing one character into the input field
 
Render: App
复制代码

简而言之,这就是 React 的memo功能。 似乎我们不需要记住 ListItem 组件。 但是,一旦您使用按钮将新项目添加到列表中,您将看到当前实现的以下输出:

// after adding an item to the list
 
Render: App
Render: List
Render: ListItem
Render: ListItem
Render: ListItem
复制代码

通过向列表中添加项目,列表会发生变化,从而导致 List 组件更新。 现在这是我们想要的行为,因为我们想要渲染所有项目(2 个项目)加上新项目(1 个项目)。 但也许只渲染一个新项目而不是所有项目会更有效:

const List = React.memo(({ list }) => {
  console.log('Render: List');
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
});
 
const ListItem = React.memo(({ item }) => {
  console.log('Render: ListItem');
  return <li>{item.name}</li>;
});
复制代码

在尝试之前的场景后,通过向列表中添加一个项目,使用 React 的 memo 功能的新实现,您应该看到以下输出:

// after adding an item to the list
 
Render: App
Render: List
Render: ListItem
复制代码

仅呈现新项目。 列表中所有以前的项目保持不变,因此不会重新渲染。 现在只有受状态影响的组件才会重新渲染。

您可能想知道为什么不在所有组件上使用 React memo,或者为什么 React memo 不是所有 React 组件的默认设置。

在内部,React 的 memo 函数必须将之前的 props 与新的 props 进行比较,以决定是否应该重新渲染组件。 通常,这种比较的计算可能比重新渲染组件更昂贵。

总之,当你的 React 组件变慢并且你想提高它们的性能时,React 的备忘录功能会大放异彩。 这通常发生在数据量大的组件中,例如巨大的列表,一旦单个数据点发生变化,许多组件必须重新渲染。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享