使用React的useCallback
钩子本质上只是一个专门用于函数的useMemo
的 Package 器,以避免在组件的props中不断创建新的函数示例。我的问题来自于何时需要将argumed传递给从memorization创建的回调。
例如,像这样创建的回调...
const Button: React.FunctionComponent = props => {
const onClick = React.useCallback(() => alert('Clicked!'), [])
return <button onClick={onClick}>{props.children}</button>
}
是一个记忆回调的简单例子,不需要传递任何外部值来完成它的工作。但是,如果我想为React.Dipatch<React.SetStateAction>
函数类型创建一个通用的记忆回调,那么它需要参数...例如:
const Button: React.FunctionComponent = props => {
const [loading, setLoading] = React.useState(false)
const genericSetLoadingCb = React.useCallback((x: boolean) => () => setLoading(x), [])
return <button onClick={genericSetLoadingCb(!loading)}>{props.children}</button>
}
在我的脑海里,这似乎是完全一样的做以下...
const Button: React.FunctionComponent = props => {
const [loading, setLoading] = React.useState(false)
return <button onClick={() => setLoading(!loading)}>{props.children}</button>
}
这将使记忆该函数的目的失败,因为由于genericSetLoadingCb(false)
也将仅在每次呈现时返回新函数,所以它仍将在每次呈现时创建新函数。
这种理解是正确的吗?或者说,用论证描述的模式仍然保留了记忆化的好处吗?
1条答案
按热度按时间dgiusagp1#
我将用一个稍微不同的用例来回答这个问题,但它仍然包含对您的问题的回答。
动机和问题陈述
让我们考虑以下高阶函数
genericCb
(类似于genericSetLoadingCb
):假设我们在以下情况下使用它,其中
Input
是使用React.memo创建的记忆组件:由于
Input
是记忆组件,我们可能希望**genericCb('firstName')
生成的**函数在重新渲染时保持不变,这样记忆组件就不会不必要地重新渲染。下面我们将看到如何实现这一点。溶液
现在,我们构建上述
genericCb
的方法是确保它在渲染中保持相同(由于使用了useCallback
)。但是,每次调用
genericCb
创建新函数时,如下所示:返回的函数在每个渲染上仍然是不同的。为了确保返回的函数对于某些输入是记忆的,你应该额外使用一些记忆方法:
现在,如果您调用
genericCb("firstName")
来生成一个函数,它将在每次渲染时返回相同的函数,前提是"firstName"
也保持不变。备注
正如在上面的注解中指出的,使用
useCallback
的解决方案似乎会产生警告(尽管在我的项目中没有):React Hook useCallback收到一个依赖关系未知的函数。请改为传递内联函数
看起来警告是存在的,因为我们没有传递内联函数给
useCallback
。我找到的基于this github线程消除这个警告的解决方案是使用useMemo
来模拟useCallback
,如下所示:简单地使用memoize而不使用
useCallback
(或者更新中的useMemo
)是不起作用的,因为在下一次渲染时,它将从fresh调用memoize,如下所示: