使用空数组作为输入的 `useCallback` 和没有第二个参数的 `useCallback` 之间有什么区别? [英] What's the difference between `useCallback` with an empty array as inputs and `useCallback` without a second parameter?

查看:18
本文介绍了使用空数组作为输入的 `useCallback` 和没有第二个参数的 `useCallback` 之间有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试更好地理解 React Hooks 的过程中,我遇到了一些我没想到的行为.我试图创建一个 refs 数组并通过 onRef 函数推送到所述数组,我将传递给我的 <div>'s.每次重新渲染组件时,数组都会变得越来越大,大概只是因为它是一个简单的箭头函数而不是记忆.

然后我添加了 useCallback 钩子以确保我不会多次获得相同的 ref,但令我惊讶的是它仍然在每次重新渲染时调用该函数.添加一个空数组作为第二个参数后,refs 只按预期为每个组件触发一次.

此行为在下面的代码段中进行了演示.

const Example = () =>{const _refs = React.useRef([]);//Var 强制重新渲染.const [ forceCount, forceUpdate ] = React.useState(0);const onRef = (ref) =>{if (ref && ref !== null) {console.log("添加引用 -> 只是一个箭头函数");_refs.current.push(ref);}}const onRefCallbackWithoutInputs = React.useCallback((ref) => {if (ref && ref !== null) {console.log("添加引用 -> 没有输入的回调.");_refs.current.push(ref);}});const onRefCallbackEmptyArray = React.useCallback((ref) => {if (ref && ref !== null) {console.log("添加引用 -> 空数组回调");_refs.current.push(ref);}}, []);React.useEffect(() => {console.log("引用大小:", _refs.current.length);});返回 (<div><div ref={onRef}/><div ref={onRefCallbackWithoutInputs}/><div ref={onRefCallbackEmptyArray}/><div onClick={() =>forceUpdate(forceCount + 1)}风格 = {{宽度:'100px',高度:'100px',marginTop: '12px',背景颜色:'橙色'}}>{'点击我更新'}

);};ReactDOM.render(, document.getElementById('root'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script><div id='root' style='width: 100%;高度:100%'>

我假设 useCallback 将有一个空数组作为第二个参数的默认值.那么不给出第二个参数到底是做什么的呢?为什么它的行为不同?

解决方案

对于 useMemouseCallback(本质上只是 useMemo),如果第二个参数是一个空数组,则该值将被记忆一次并始终返回.

如果省略第二个参数,则永远不会记住该值,并且 useCallbackuseMemo 不会做任何事情.

也许在某些极端情况下您可以有条件地记住:

useMemo(someValue, shouldMemoize ? [] : null)

但在绝大多数情况下,useMemouseCallback 的第二个参数应该被认为是强制性的.事实上,.

//需要第二个参数,而且必须是数组函数 useCallback任何>(回调:T,deps:DependencyList):T;//第二个参数可以未定义,但必须显式传递为未定义,不能省略.function useMemo(factory: () => T, deps: DependencyList | undefined): T;

<小时>

有一个开放拉取请求增强了exhaustive-depscode> 钩住 eslint 规则,如果第二个参数被省略,它会引发一个 lint 错误,所以很快这很可能是一个 linter 错误.

On my journey to try and understand React Hooks better I came across some behaviour I did not expect. I was attempting to create an array of refs and pushing to said array via an onRef function I would pass to my <div>'s. The array kept getting bigger everytime the component re-rendered presumably just because it was a simple arrow function and not memoized.

So then I added the useCallback hook to make sure that I wouldn't get the same ref multiple times, but to my surprise it still called the function every re-render. After adding an empty array as second parameter the refs only fired once per component as expected.

This behaviour is demonstrated in the snippet below.

const Example = () => {
  const _refs = React.useRef([]);
  
  // Var to force a re-render.
  const [ forceCount, forceUpdate ] = React.useState(0);
  
  const onRef = (ref) => {
    if (ref && ref !== null) {
      console.log("Adding Ref -> Just an arrow function");
      _refs.current.push(ref);
    }
  }
  
  const onRefCallbackWithoutInputs = React.useCallback((ref) => {
    if (ref && ref !== null) {
      console.log("Adding Ref -> Callback without inputs.");
      _refs.current.push(ref);
    }
  });
  
  const onRefCallbackEmptyArray = React.useCallback((ref) => {
    if (ref && ref !== null) {
      console.log("Adding Ref -> Callback with empty array");
      _refs.current.push(ref);
    }
  }, []);
  
  React.useEffect(() => {
    console.log("Refs size: ", _refs.current.length);
  });
  
  return (
    <div>
      <div ref={onRef}/>
      <div ref={onRefCallbackWithoutInputs}/>
      <div ref={onRefCallbackEmptyArray}/>
      <div onClick={() => forceUpdate(forceCount + 1)} 
        style = {
          {
            width: '100px',
            height: '100px',
            marginTop: '12px',
            backgroundColor: 'orange'
          }
        }>
        {'Click me to update'}
       </div>
    </div>
  );
};

ReactDOM.render(<Example/>, document.getElementById('root'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>

<div id='root' style='width: 100%; height: 100%'>
</div>

I assumed useCallback would have an empty array as a default for the second parameter. So what exactly does not giving a second parameter do? Why does it behave differently?

解决方案

For both useMemo and useCallback (which is essentially just a special case of useMemo), if the second argument is an empty array, the value will be memoized once and always returned.

If the second argument is omitted, the value will never be memoized, and the useCallback and the useMemo doesn't do anything.

Perhaps there's some edge case where you might conditionally memoize:

useMemo(someValue, shouldMemoize ? [] : null)

But in the vast majority of cases, the second argument to both useMemo and useCallback should be considered mandatory. And in fact, the Typescript definitions treat them this way.

// Require a second argument, and it must be an array
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;

// Second argument can be undefined, but must be explicitly passed as undefined, not omitted.
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;


There's an open pull request that's enhancing the exhaustive-deps hooks eslint rule so that it will raise a lint error if the second argument is omitted, so pretty soon this will likely be a linter error.

这篇关于使用空数组作为输入的 `useCallback` 和没有第二个参数的 `useCallback` 之间有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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