我对
useCallback
的工作原理有相当不错的了解。不过,对于我和我的同事来说,弄清楚何时使用它似乎是主观的。我很好奇其他人对我们当前的困境有何看法。
假设我们有一个组件,由于选择了某些内容而正在分派一个操作以进行 redux:
const SelectionComponent = props => {
const dispatch = useDispatch()
const handleSelect = (selection) => {
dispatch(actions.updateSelection(selection))
}
return <Select onSelect={handleSelect} ... />
}
我的同事认为我们应该将
handleSelect
包装在 useCallback
中,以确保该函数具有稳定的标识,因为它作为回调传递给子组件:
const SelectionComponent = props => {
const dispatch = useDispatch()
const handleSelect = useCallback((selection) => {
dispatch(actions.updateSelection(selection))
}, [dispatch])
return <Select onSelect={handleSelect} ... />
}
所以我的问题是,哪个是更好的解决方案,为什么?
一些注意事项:
dispatch
函数澄清一下,这是一个关于我们是否应该在传递给子组件时保持稳定身份的基础上记住函数的问题,即使组件树不会因任何原因重新渲染。
仅当
handleSelect
也被记忆时,记忆 Select
才会产生影响。请记住 - 当一个组件重新渲染时,默认情况下它也会重新渲染其子组件的所有(无论它们的 props 是否改变)。
因此,在不知道
Select
是如何实现的情况下,我们无法真正判断 useCallback
是否“更好”,实际上是否有任何影响。
通常,这种类型的优化是不必要的。除非您的
Select
在某种程度上很复杂或昂贵,否则您可能也不需要记住。
同样的问题, 我刚刚找到答案https://github.com/reduxjs/react-redux/issues/1468 “dispatch 将始终是相同的函数引用。(事实上,在 React-Redux 的早期版本中,禁止传递新的存储引用。我们现在支持在运行时更改它,但实际上您可能不会这样做.)
但是,ESLint 规则并不知道这一点 - 它只知道调度不是内置的 React hook 返回值,因此它可能会发生变化,因此它告诉您应该将其添加到依赖项数组中,以防万一它确实会改变。”
当组件重新渲染时,如果其所有子组件的任何属性发生更改,它都会重新渲染。意思是,如果一个孩子具有与以前相同的属性,则会触发渲染,这是真的,但它实际上不会进行任何更改,也不会花费任何时间。
但是,
useCallback
依赖项检查也是有成本的,因此由您决定,使用useCallback
的开销是否比重新渲染Select
组件更便宜。考虑到它还有其他属性可能会改变并导致它重新渲染。
我更喜欢记住回调,除非它们被传递给“本机”组件。因为我无法确定子组件是否不会使用此回调作为依赖项。
我想也许答案就在这里:https://github.com/reduxjs/react-redux/issues/1468
即
useDispatch
的实现如下:
const useDispatch = () => {
const store = useStore();
return store.dispatch;
}
讨论基本上涉及指出商店实际上应该只有一个实例(永远)并且它不会被重新渲染。我也有同样的疑问/问题。