useCallback如何使用(How to useCallback in React)

React的useCallback Hook可用于优化React函数组件的渲染行为。我们先通过一个示例组件来说明问题,然后使用React的useCallback Hook解决该问题。
请记住,React中的大多数性能优化还为时过早。默认情况下,React是快速的,因此所有性能优化都是可选的,以防万一开始变慢。
注意:不要将React的useCallback Hook与React的useMemo Hook混为一谈。尽管useCallback用于储存functions,但useMemo用于储存values
注意:不要将React's memo API与React的useCallback Hook混淆。useCallback被用来储存函数,而React memo用于包装React组件以防止多余重新渲染。
让我们以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 }));
  };
 
  const handleRemove = (id) => {
    setUsers(users.filter((user) => user.id !== id));
  };
 
  return (
    <div>
      <input type="text" value={text} onChange={handleText} />
      <button type="button" onClick={handleAddUser}>
        Add User
      </button>
 
      <List list={users} onRemove={handleRemove} />
    </div>
  );
};
 
const List = ({ list, onRemove }) => {
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} onRemove={onRemove} />
      ))}
    </ul>
  );
};
 
const ListItem = ({ item, onRemove }) => {
  return (
    <li>
      {item.name}
      <button type="button" onClick={() => onRemove(item.id)}>
        Remove
      </button>
    </li>
  );
};
 
export default App;
使用我们了解的关于React memo的知识(如果您不了解React memo,请先阅读指南,然后再回来),它与我们的示例具有相似的组件,我们希望防止用户输入时重新渲染每个组件。
const App = () => {
  console.log('Render: App');
 
  ...
};
 
const List = ({ list, onRemove }) => {
  console.log('Render: List');
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} onRemove={onRemove} />
      ))}
    </ul>
  );
};
 
const ListItem = ({ item, onRemove }) => {
  console.log('Render: ListItem');
  return (
    <li>
      {item.name}
      <button type="button" onClick={() => onRemove(item.id)}>
        Remove
      </button>
    </li>
  );
};
键入用于将项目添加到列表的输入字段仅应触发App组件的重新渲染,而不触发不关心此状态更改其子组件。因此,React memo将用于防止子组件更新:
const List = React.memo(({ list, onRemove }) => {
  console.log('Render: List');
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} onRemove={onRemove} />
      ))}
    </ul>
  );
});
 
const ListItem = React.memo(({ item, onRemove }) => {
  console.log('Render: ListItem');
  return (
    <li>
      {item.name}
      <button type="button" onClick={() => onRemove(item.id)}>
        Remove
      </button>
    </li>
  );
});
但是,也许令您奇怪的是,在Input标签中输入时,两个功能组件仍会重新渲染。对于在Input标签中输入的每个字符,您仍然应该看到与以前相同的输出:
// after typing one character into the input field
 
Render: App
Render: List
Render: ListItem
Render: ListItem
让我们看一下传递给List组件的props。
const App = () => {
  // How we're rendering the List in the App component 
  return (
    //...
    <List list={users} onRemove={handleRemove} />
  )
}
只要未在列表props中添加或删除任何项目,即使用户在Input标签中输入内容后App组件重新渲染,该项目也应保持不变。因此,罪魁祸首是onRemove回调处理程序。
每当有人在Input标签输入内容后,App组件重新呈现时,都会重新定义App中的handleRemove处理函数。
通过将此新的回调处理程序作为props传递给List组件,它注意到与以前的渲染相比,props已更改。这就是为什么要重新启动List和ListItem组件的原因。
最后,我们有了React的useCallback Hook的用例。我们可以使用useCallback来保存一个函数,这意味着仅当依赖项数组中的依赖项发生更改时,该函数才会被重新定义:
const App = () => {
  ...
  // Notice the dependency array passed as a second argument in useCallback
  const handleRemove = React.useCallback(
    (id) => setUsers(users.filter((user) => user.id !== id)),
    [users]
  );
 
  ...
};
如果用户通过在列表中添加或删除项目来更改状态,则将重新定义处理函数,并且子组件也应重新渲染。 但是,如果某人仅在Input标签中输入内容,则该函数不会重新定义并且保持不变。因此,子组件不会收到更改的props,并且在这种情况下不会重新渲染。
您可能想知道为什么不对所有函数都使用React的useCallback Hook,或者为什么为什么React的useCallback Hook并不是所有函数的默认设置。 在内部,React的useCallback Hook必须比较依赖数组中每个重新渲染的依赖项,以决定是否应重新定义该函数。通常,用于此比较的计算可能比仅重新定义函数要的时间多。
总之,React的useCallback Hook用于记忆函数。将函数传递给其他组件时,不必担心为父组件的每次重新渲染而重新初始化该函数,这已经是很小的性能提升。但是,正如您所看到的,当与React的备忘API一起使用时,React的useCallback Hook开始发光。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值