使用React的useCallback
钩子实际上只是useMemo
的包装,专门用于函数,以避免在组件的props中不断创建新的函数实例。我的问题来自何时需要将争论传递给基于备忘录创建的回调。
例如,这样创建的回调...
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)
还将在每个渲染上都返回一个新功能,因此它仍将在每个渲染上创建一个新功能。
这种理解是正确的,还是用参数描述的模式仍然保持记忆的好处?
答案 0 :(得分:12)
为了下面的示例,我将使用genericCb
函数而不是genericSetLoadingCb
。
const genericCb = React.useCallback((param) => () => someFunction(param), [])
我们在上面所做的工作是确保功能genericCb
在所有rerender中保持不变。但是,每次您用这样的方法创建新功能时:
genericCb("someParam")
返回的函数将在每个渲染器上不同。 为了确保记住返回的函数,您需要执行以下操作:
let memoizedCb = React.useCallback(
memoize((param) => () => someFunction(param)),
[]
);
我使用快速记忆,例如
import memoize from "fast-memoize";
现在,如果您使用memoizedCb("someParam")
生成一个函数,只要"someParam"
也保持不变,它将在每个渲染器上返回相同的函数。
仅使用没有useCallback
的备忘录是行不通的,因为在下一次渲染时,它将像这样从新调用备忘录:
let memoized = memoize(fn)
memoized('foo', 3, 'bar')
memoized('foo', 3, 'bar') // cache hit
memoized = memoize(fn); // without useCallback this would happen on next render
// Now the previous cache is lost
注意:正如@Jeaf在Typescript中指出的那样,这种方法似乎会产生警告
React Hook useCallback收到了一个依赖项为 未知。改为通过内联函数
我本人不使用Typescript,因此我不确定处理此警告的最佳方法是什么。似乎存在警告,因为useCallback
无法看到依赖关系,因此无法警告您可能的过时关闭。在这种情况下,似乎由用户决定。
答案 1 :(得分:0)
执行以下操作似乎是解决问题的一种优雅而简单的方法。如果Button仅在重新渲染,它将不会创建新的cb函数。
const Button = props => {
const [loading, setLoading] = React.useState(false)
const cb = React.useCallback(() => { setLoading(!loading) }, [loading]);
return <button onClick={cb}>{props.children}</button>
}