用参数反应useCallback

时间:2020-04-16 16:20:06

标签: reactjs callback react-hooks

使用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)还将在每个渲染上都返回一个新功能,因此它仍将在每个渲染上创建一个新功能。

这种理解是正确的,还是用参数描述的模式仍然保持记忆的好处?

2 个答案:

答案 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>
}