使用参数反应 useCallback [英] React useCallback with Parameter

查看:20
本文介绍了使用参数反应 useCallback的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 React 的 useCallback 钩子本质上只是对专门用于函数的 useMemo 的包装,以避免在组件的 props 中不断创建新的函数实例.我的问题来自何时需要将争论传递给根据记忆创建的回调.

Using React's useCallback hook is essentially just a wrapper around useMemo specialized for functions to avoid constantly creating new function instances within components' props. My question comes from when you need to pass an argued to the callback created from the memoization.

例如,像这样创建的回调...

For instance, a callback created like so...

const Button: React.FunctionComponent = props => {
    const onClick = React.useCallback(() => alert('Clicked!'), [])
    return <button onClick={onClick}>{props.children}</button>
}

是记忆化回调的一个简单示例,不需要将外部值传递给它来完成其工作.但是,如果我想为 React.Dipatch 函数类型创建一个通用的记忆回调,那么它需要参数......例如:

is a simple example of a memoized callback and required no external values passed into it in order to accomplish its job. However, if I want to create a generic memoized callback for a React.Dipatch<React.SetStateAction> function type, then it would require arguments...for example:

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

在我看来,这似乎与执行以下操作完全相同...

In my head, this seems like its the exact same as doing the following...

const Button: React.FunctionComponent = props => {
    const [loading, setLoading] = React.useState(false)
    return <button onClick={() => setLoading(!loading)}>{props.children}</button>
}

这会让记忆函数的目的落空,因为它仍然会在每次渲染时创建一个新函数,因为 genericSetLoadingCb(false) 也会在每次渲染时返回一个新函数.

which would let defeat the purpose of memoizing the function because it would still be creating a new function on every render since genericSetLoadingCb(false) would just be returning a new function on each render as well.

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

Is this understanding correct, or does the pattern described with arguments still maintain the benefits of memoization?

推荐答案

动机和问题陈述

让我们考虑以下(类似于您的genericSetLoadingCb)高阶函数genericCb:

  const genericCb = React.useCallback(
    (param) => (e) => setState({ ...state, [param]: e.target.value }),
    []
  );

genericCb 在以下情况下很有用,其中 Input 是使用 React.memo:

genericCb can be useful in the following situation where Input is a memoized component created using React.memo:

  <Input value={state.firstName} onChange={genericCb('firstName')} />

由于 Input 是记忆化组件,我们可能希望 genericCb('firstName') 生成的函数在重新渲染时保持不变(以便记忆化组件不会不必要地重新渲染).下面我们将看到如何实现这一点.

Since Input is memoized component, we may want the function generated by genericCb('firstName') to remain the same across re-renders (so that the memoized component doesn't re-render needlessly). Below we will see how to achieve this.

注意,如果我们在这个例子中没有使用 genericCb,并且我们有很多 Input 元素,我们需要为每个元素创建一个事件处理程序单独使用 useCallback.

Note that if we didn't use genericCb in this example, and we had many Input elements, we'd need to create an event handler for each of them using useCallback separately.

现在,我们在上面构建 genericCb 的方式是确保它在渲染中保持相同(由于使用了 useCallback).

Now, the way we constructed genericCb above is we ensured that it remains the same across renders (due to usage of useCallback).

然而,每次你调用 genericCb 来创建一个新的函数时,就像这样:

However, each time you call genericCb to create a new function out of it like this:

genericCb("firstName") 

返回的函数在每次渲染时仍然不同.为了确保返回的函数被记忆用于某些输入,你应该另外使用一些记忆方法:

The returned function will still be different on each render. To also ensure the returned function is memoized for some input, you should additionally use some memoizing approach:

  import memoize from "fast-memoize";
  ....

  const genericCb = React.useCallback(
    memoize((param) => (e) => setState({ ...state, [param]: e.target.value })),
    []
  );

现在,如果您调用 genericCb("firstName") 来生成一个函数,它将在每次渲染时返回相同的函数,前提是 "firstName"也保持不变.

Now if you call genericCb("firstName") to generate a function, it will return same function on each render, provided "firstName" also remains the same.

正如上面使用 useCallback 解决方案的评论中所指出的,似乎会产生警告(虽然在我的项目中没有):

As pointed out in the comments above solution using useCallback seems to produce warning (it didn't in my project though):

React Hook useCallback 收到一个函数,它的依赖是未知.改为传递内联函数

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead

似乎警告是因为我们将 memoize 传递给 useCallback.我发现基于 this github 线程摆脱此警告的解决方案是使用useMemo 像这样模仿useCallback:

It seems the warning is there because we passed memoize to useCallback. The solution I found to get rid of this warning based on this github thread is to use useMemo to imitate useCallback like this:

// Use this; this doesn't produce the warning anymore  
const genericCb = React.useMemo(
    () =>
      memoize(
        (param) => (e) => setState({ ...state, [param]: e.target.value })
      ),
    []
  );


仅使用没有 useCallback(或更新中的 useMemo)的 memoize 是行不通的,因为在下一次渲染时它会像这样从新调用 memoize:


Simply using memoize without useCallback (or useMemo as in the update) wouldn't work, as on next render it would invoke memoize from fresh like this:

let memoized = memoize(fn)
 
memoized('foo', 3, 'bar')
memoized('foo', 3, 'bar') // cache hit

memoized = memoize(fn); // without useCallback (or useMemo) this would happen on next render 

// Now the previous cache is lost

这篇关于使用参数反应 useCallback的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆