React ref callbacks在使用`useCallback`钩子时会有不同的行为吗?

huangapple go评论65阅读模式
英文:

React ref callbacks behaves differently with useCallback hook?

问题

在我的React组件中,使用了类似下面这样的useCallback钩子。我使用useCallback包装了一个onClick事件处理程序,并使用useCallback包装了一个ref回调函数。在单击按钮时,会更改状态值,从而导致组件重新渲染。观察到在onClick事件处理程序"handleOnClick"内部的控制台日志在每次重新渲染时都会执行,而在ref回调函数"divRefCallback"内部的控制台日志只执行一次。

我可以理解为什么"handleOnClick"在每次重新渲染时都会执行,因为useCallback只会避免重新创建函数,但不会避免重新执行函数。然而,我不能理解为什么useCallback在处理ref回调时表现不同,因为在这个示例中,它既不重新创建函数,也不执行回调内部的逻辑。

我想知道为什么它会表现成这样?是否有其他情况下,useCallback会避免重新执行回调逻辑?

是否有官方文档可以解释这个问题?提前感谢任何见解!

function UseCallbackDemo() {
  const divRef = React.useRef<HTMLDivElement | null>(null);
  const [count, setCount] = React.useState(0);

  console.log("**** 重新渲染组件");
  const divRefCallback = React.useCallback((div) => {
    if (div) {
      divRef.current = div;
    }
    console.log("*** divRefCallback");
  }, []);

  const handleOnClick = React.useCallback(() => {
    console.log('**** handleOnClick');
    setCount(count => count + 1);
  }, []);

  return (
    <>
      <div ref={divRefCallback}>{count}</div>
      <button onClick={handleOnClick}>点击</button>
    </>
  );
}

请在这里测试代码:https://playcode.io/1529685

英文:

In my React component with useCallback hook like below. I wrapped a onClick event handler with useCallback also wrapped a ref callback with useCallback. While clicking on the button, it would change the state value hence cause the component to re-render. It's observed that the console log inside the onClick event handler "handleOnClick" is executed at every re-render, whereas the console log inside the ref callback "divRefCallback" only executed once.

I can understand why the handleOnClick is executed at each re-render, because useCallback will only avoid recreating the function, but will NOT avoid re-executing the function. However, I cannot understand why useCallback works differently with ref callback, because in this example, it doesn't re-create the function but also not executing the logic inside the callback as well.

I am wondering why it's behaving like this? Is there any other scenario where useCallback would avoid re-executing the callback logic?

Is there any official document to explain this? Thanks in advance for any insights!

function UseCallbackDemo() {
  const divRef = React.useRef&lt;HTMLDivElement | null&gt;(null);
  const [count, setCount] = React.useState(0);

  console.log(&quot;**** re-render component&quot;);
  const divRefCallback = React.useCallback((div) =&gt; {
    if (div) {
      divRef.current = div;
    }
    console.log(&quot;*** divRefCallback&quot;);
  }, []);

  const handleOnClick = React.useCallback(() =&gt; {
    console.log(&#39;**** handleOnClick&#39;);
    setCount(count =&gt; count + 1);
  }, []);

  return (
    &lt;&gt;
      &lt;div ref={divRefCallback}&gt;{count}&lt;/div&gt;
      &lt;button onClick={handleOnClick}&gt;click&lt;/button&gt;
    &lt;/&gt;
  );
}

Please test the code here: https://playcode.io/1529685

答案1

得分: 2

根据文档

<blockquote>
不要使用 ref 对象(类似于由 <code>useRef</code> 返回的对象),可以将函数传递给 ref 属性。<br> <br>
<pre><code>&lt;div ref={(node) => console.log(node)} /></code></pre>
当 &lt;div> DOM 节点添加到屏幕时,React 将以 DOM 节点作为参数调用您的 ref 回调函数。当该 &lt;div> DOM 节点被移除时,React 将以 null 调用您的 ref 回调函数。<br> <br>
每当传递不同的 ref 回调时,React 也会调用您的 ref 回调函数。在上面的示例中,<code>(node) => { ... }</code> 每次渲染时都是不同的函数。当您的组件重新渲染时,以前的函数将以 null 作为参数被调用,然后下一个函数将以 DOM 节点作为参数被调用。
</blockquote>

由于 useCallback 在依赖数组为空时每次都返回相同的函数引用,因此在问题示例中,ref 函数仅在 &lt;div&gt; 被移除之前被调用一次。

英文:

According to the documentation:

<blockquote>
Instead of a ref object (like the one returned by <code>useRef</code>), you may pass a function to the ref attribute. <br> <br>
<pre><code>&lt;div ref={(node) => console.log(node)} /></code></pre>
When the &lt;div> DOM node is added to the screen, React will call your ref callback with the DOM node as the argument. When that &lt;div> DOM node is removed, React will call your ref callback with null. <br> <br>
React will also call your ref callback whenever you pass a different ref callback. In the above example, <code>(node) => { ... }</code> is a different function on every render. When your component re-renders, the previous function will be called with null as the argument, and the next function will be called with the DOM node.
</blockquote>

Since useCallback returns the same function reference each time when the dependency array is empty, the ref function is only called once (before the &lt;div&gt; is removed) in the example from the question.

huangapple
  • 本文由 发表于 2023年7月11日 09:33:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76658221.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定