将useRef用作实例变量时useCallback和useRef挂钩之间的区别

时间:2019-09-10 12:25:12

标签: reactjs react-hooks

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

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

根据我对this answer on stack overflow的理解,以下函数可以写为:

  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") }
      ]}
    />
  );
};

替换是否也有意义?

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

具有:

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

所以它仅在urlToNavigate更改时更改吗?

2 个答案:

答案 0 :(得分:1)

在问题中引用的链接中,关键是子组件(接收回调的组件)已被记忆

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

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

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

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


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

答案 1 :(得分:0)

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

您的回调函数具有history作为依赖项,因此您可以这样做

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

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