关于reactjs:将useRef用作实例变量时useCallback和useRef钩子之间的区别

Difference between useCallback and useRef hooks when useRef is used as an instance variable

我正在阅读有关React中的钩子的信息,并且在理解useRef和useCallback钩子之间的区别时遇到了一些麻烦。

具体地说,我想了解如何使用这两个方法来避免不必要地重新渲染子组件。

基于我对堆栈溢出的答案的理解,可以将以下函数编写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  const Avatar = function({ history, url, fullName }) {
  const onMenuItemClick = urlToNavigate => history.push(urlToNavigate),
    onMenuItemClickRef = useRef(onMenuItemClick);

  return (
    <Menu
      label={<RoundedImage src={url} alt={fullName} />}
      items={[
        { label: `Logged in as: ${fullName}` },
        {
          label:"Liked articles",
          onClick: () => onMenuItemClickRef.current("/liked-articles")
        },
        {
          label:"Edit profile",
          onClick: () => onMenuItemClickRef.current("/profile")
        },
        { label:"Logout", onClick: () => console.log("Logging out") }
      ]}
    />
  );
};

替换是否也有意义:

1
const onMenuItemClick = urlToNavigate => history.push(urlToNavigate)

具有:

1
const onMenuItemClick = useCallBack(urlToNavigate => history.push(urlToNavigate), [urlToNavigate])

是否仅在urlToNavigate更改时更改?


在问题中引用的链接中,关键是记住了子组件(接收回调的组件)。

在正常的"父代">"子代"周期中,每次"父代"的道具更改都会重新渲染,即使子代的任何道具都没有更改,子代也会重新渲染。

如果我们希望避免在不更改其prop的情况下渲染Child,则我们可以记住该组件(React.memo)。一旦这样做,我们可能会遇到一个问题,如果在Parent中为每个渲染创建一个函数回调,因为记忆的Children将被解释为一个新函数,因此将再次渲染。

为避免这种情况,我们可以在Parent中使用useCallback,因此,当子级收到回调时,它会知道该回调是新的并且应该渲染。

在未记忆孩子的情况下使用useCallback并没有帮助,因为如果Parent渲染,则无论如何孩子都会渲染。

关于使用useRef作为该函数,我不确定,但是我想说这可能类似于使用带有空数组作为依赖项的useCallback


不,这不起作用。您只能记住来自外部作用域的值,但是urlToNavigate是提供给回调本身的参数。如果您使用例如在回调中传递给组件的属性,您可以将其作为依赖项提供。

您的回调具有history作为依赖项,因此您可以执行

1
const onMenuItemClick = useCallback(urlToNavigate => history.push(urlToNavigate), [history])

这样,不会在每个渲染器上都重新创建回调,而仅在history属性更改时才重新创建。