我正在阅读有关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更改时更改吗?
答案 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
属性更改时重新创建回调。