React hooks:通常不需要 useCallback 吗?
React hooks: is `useCallback` not so needed usually?
我最近正在使用 React Hooks 重构一个 Web 应用程序。我遇到了关于 useCallback
的问题。根据Kent的描述:https://kentcdodds.com/blog/usememo-and-usecallback,useCallback
就是给子组件传入相同的函数引用,避免子组件重新渲染,这样性能更好。但是,它与 React.memo
一起使用。正如肯特所说:
MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. In fact, the need to optimize stuff with what I'm about to show you is so rare that I've literally never needed to do it ...
所以,我的问题是:我声称我们通常不需要使用 useCallback
是否正确?除非回调的创建成本很高,否则使用 useCallback
可避免为每个渲染重新创建回调。
比如说,对于 onClick
或 onChange
事件处理程序,2 行或更少,我们是否可以不使用 useCallback
来包装它?
当我不想更改函数引用时,我发现 useCallback()
是必需的。例如,当我在某些子组件上使用 React.memo
时,由于 props.
对其方法之一的引用更改,不应重新渲染这些子组件
示例:
在下面的示例中,如果 Parent 重新渲染,Child1
将始终重新渲染,因为 parentMethod1
将在每次渲染时获得新的引用。并且 Child2
不会重新渲染,因为 parentMethod2
将在渲染中保留其引用(您可以传递依赖项数组以使其在新输入值出现时更改并重新创建)。
注意: 假设 Child
组件被存储为 React.memo()
function Parent() {
const parentMethod1 = () => DO SOMETHING;
const parentMethod2 = useCallback(() => DO SOMETHING,[]);
return(
<React.Fragment>
<Child1
propA=parentMethod1
/>
<Child2
propA=parentMethod2
/>
</React.Fragment>
);
}
另一方面,如果 function
比 运行 昂贵,您可以使用 useMemo
钩子记住它的结果。然后你只会在新值出现时 运行 它,否则它会给你一个使用相同值的先前计算的记忆结果。
https://reactjs.org/docs/hooks-reference.html#usecallback
useCallback
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate
).
useMemo
Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
我认为你是对的。从它的设计方式来看,useCallback
在 React 中应该几乎没有用。它不能直接用于防止子渲染。
可以保存子渲染的方法是使用 useMemo
.
包裹整个渲染
const Title = () => {
...
const child = useMemo(() => {
return <Child a={"Hello World"} />
}, [])
return (
<>
{child}
<div onClick={onClick}>{count}</div>
</>
)
}
上述方法与React.memo
有点不同,因为它直接作用于父Title,而不是Child。但它或多或少地回答了你为什么它没用的问题,除非你将它用作 useMemo
.
的快捷方式
解释这一点的文章,https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981
回到 useCallback
现在让我们回过头来看看使用或不使用 useCallback
包装的回调是否有用。
<div onClick={onClick}>kk</div>
它唯一可以保存的是当它处于协调状态时,onClick
(使用 useCallback)指向同一个函数实例。
但是我不知道 React 是否真的在那一步做了任何优化。因为为属性分配不同的回调可能会占用额外的内存和时间。但是添加一个新变量通常也会占用额外的内存。
所以这种优化更像是编码优化,或多或少是主观的。 objective 不足以应用于可靠的案例。
当然,如果你想修复任何第三方函数的函数实例,例如。 debounce
。这可能是一个很好的用途,但仍然闻起来有腥味,因为 useMemo
似乎也更通用,可以涵盖这种情况。
总而言之,我只是指出,useCallback
没有做 public 认为它可以做的事情,例如救助子组件。
我最近正在使用 React Hooks 重构一个 Web 应用程序。我遇到了关于 useCallback
的问题。根据Kent的描述:https://kentcdodds.com/blog/usememo-and-usecallback,useCallback
就是给子组件传入相同的函数引用,避免子组件重新渲染,这样性能更好。但是,它与 React.memo
一起使用。正如肯特所说:
MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. In fact, the need to optimize stuff with what I'm about to show you is so rare that I've literally never needed to do it ...
所以,我的问题是:我声称我们通常不需要使用 useCallback
是否正确?除非回调的创建成本很高,否则使用 useCallback
可避免为每个渲染重新创建回调。
比如说,对于 onClick
或 onChange
事件处理程序,2 行或更少,我们是否可以不使用 useCallback
来包装它?
当我不想更改函数引用时,我发现 useCallback()
是必需的。例如,当我在某些子组件上使用 React.memo
时,由于 props.
示例:
在下面的示例中,如果 Parent 重新渲染,Child1
将始终重新渲染,因为 parentMethod1
将在每次渲染时获得新的引用。并且 Child2
不会重新渲染,因为 parentMethod2
将在渲染中保留其引用(您可以传递依赖项数组以使其在新输入值出现时更改并重新创建)。
注意: 假设 Child
组件被存储为 React.memo()
function Parent() {
const parentMethod1 = () => DO SOMETHING;
const parentMethod2 = useCallback(() => DO SOMETHING,[]);
return(
<React.Fragment>
<Child1
propA=parentMethod1
/>
<Child2
propA=parentMethod2
/>
</React.Fragment>
);
}
另一方面,如果 function
比 运行 昂贵,您可以使用 useMemo
钩子记住它的结果。然后你只会在新值出现时 运行 它,否则它会给你一个使用相同值的先前计算的记忆结果。
https://reactjs.org/docs/hooks-reference.html#usecallback
useCallback
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g.
shouldComponentUpdate
).useMemo
Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
我认为你是对的。从它的设计方式来看,useCallback
在 React 中应该几乎没有用。它不能直接用于防止子渲染。
可以保存子渲染的方法是使用 useMemo
.
const Title = () => {
...
const child = useMemo(() => {
return <Child a={"Hello World"} />
}, [])
return (
<>
{child}
<div onClick={onClick}>{count}</div>
</>
)
}
上述方法与React.memo
有点不同,因为它直接作用于父Title,而不是Child。但它或多或少地回答了你为什么它没用的问题,除非你将它用作 useMemo
.
解释这一点的文章,https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981
回到 useCallback
现在让我们回过头来看看使用或不使用 useCallback
包装的回调是否有用。
<div onClick={onClick}>kk</div>
它唯一可以保存的是当它处于协调状态时,onClick
(使用 useCallback)指向同一个函数实例。
但是我不知道 React 是否真的在那一步做了任何优化。因为为属性分配不同的回调可能会占用额外的内存和时间。但是添加一个新变量通常也会占用额外的内存。
所以这种优化更像是编码优化,或多或少是主观的。 objective 不足以应用于可靠的案例。
当然,如果你想修复任何第三方函数的函数实例,例如。 debounce
。这可能是一个很好的用途,但仍然闻起来有腥味,因为 useMemo
似乎也更通用,可以涵盖这种情况。
总而言之,我只是指出,useCallback
没有做 public 认为它可以做的事情,例如救助子组件。