首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么在使用useCallback而不是直接使用useCallback时使用'ref‘

为什么在使用useCallback而不是直接使用useCallback时使用'ref‘
EN

Stack Overflow用户
提问于 2022-08-24 01:03:49
回答 2查看 349关注 0票数 3

我正在从事React项目,我正在调查一些图书馆。我发现他们使用的‘useCallback’和我使用的不同。下面是代码部分。我仍然认为这段代码与直接使用"useCallback“没有什么区别。

代码语言:javascript
复制
// Saves incoming handler to the ref in order to avoid "useCallback hell"
export function useEventCallback<T, K>(handler?: (value: T, event: K) => void): (value: T, event: K) => void {
  const callbackRef = useRef(handler);

  useEffect(() => {
    callbackRef.current = handler;
  });

  return useCallback((value: T, event: K) => callbackRef.current && callbackRef.current(value, event), []);
}

所以我的问题是,这是什么意思'useCallback地狱‘?以这种方式使用"useCallback“有什么好处呢?

// BTW :我在react文档上找到了一个类似的例子。但我还是听不懂https://en.reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-08-24 01:22:47

执行普通useCallback时,必须传入包含函数使用的变量的依赖项数组。当其中一个改变的时候,回忆录就会破裂。在许多情况下,这是很好的,但有时你的回忆录总是在破碎(因为你依赖的是不断变化的价值观)。当这种情况发生时,useCallback提供的好处很少或根本没有。

您所展示的代码的目标是,即使您有复杂的依赖关系,回忆录也不会中断。注意,当它调用useCallback时,它传入一个空的依赖数组[]。这与使用ref来跟踪最新的handler是相结合的。然后,当函数最终被调用时,它将检查ref中最新的handler并调用它。这个最新的handler在其闭包中有最新的值,因此它的行为与预期的一样。

这段代码确实实现了永不破坏回忆录的目标。然而,它需要谨慎使用。如果使用function的并发呈现,并且在呈现过程中调用useEventCallback返回的函数,则可以得到一些意想不到的结果。只有在呈现之外调用函数才是安全的,例如在事件回调中,这就是为什么他们将其命名为useEventCallback的原因。

票数 3
EN

Stack Overflow用户

发布于 2022-08-25 22:00:50

举例说明:

部件体中的功能

代码语言:javascript
复制
function App() {
  const [count, setCount] = useState(0);
  
  const lastRenderTime = new Date().toString();
  
  function bodyFn() {
    alert("bodyFn: " + lastRenderTime);
  }

  return (
    <>
      Last render time: {lastRenderTime}
      <SomeChildComponent onClick={bodyFn} />
    </>
  );
}

每当<App>组件重新呈现时(例如,如果修改了count状态),就会创建一个新的bodyFn

如果<SomeChildComponent>监视它的onClick支柱引用(通常在依赖数组中),它每次都会看到新的创建。

但是事件回调行为与预期相同:每当调用bodyFn时,它都是该函数的“最新创建”,特别是它正确地使用了lastRenderTime的最新值(与已经显示的值相同)。

useCallback的常见用法

代码语言:javascript
复制
function App() {
  const lastRenderTime = new Date().toString();
  
  const ucbFn = useCallback(
    () => alert("ucbFn: " + lastRenderTime),
    [lastRenderTime]
  );

  return (
    <>
      Last render time: {lastRenderTime}
      <SomeChildComponent onClick={ucbFn} />
    </>
  );
}

每当<App>重新呈现时,lastRenderTime值就会不同。因此,useCallback依赖数组启动,为ucbFn创建了一个新的更新函数。

与前一种情况一样,<SomeChildComponent>每次都会看到这种变化。useCallback的使用似乎毫无意义!

但至少,这种行为也和预期的一样:函数始终是最新的,并显示正确的lastRenderTime值。

试图避免useCallback依赖

代码语言:javascript
复制
function App() {
  const lastRenderTime = new Date().toString();
  
  const ucbFnNoDeps = useCallback(
    () => alert("ucbFnNoDeps: " + lastRenderTime),
    [] // Attempt to avoid cb modification by emptying the dependency array
  );

  return (
    <>
      Last render time: {lastRenderTime}
      <SomeChildComponent onClick={ucbFnNoDeps} />
    </>
  );
}

在试图恢复useCallback优势的“天真”尝试中,人们可能会尝试将lastRenderTime从其依赖数组中删除。

现在,ucbFnNoDeps确实总是一样的,<SomeChildComponent>也不会改变。

但是现在,这种行为不再像人们所期望的那样:ucbFnNoDeps读取它创建时在其范围内的lastRenderTime的值,即呈现first time <App>

带有自定义useEventCallback钩子

代码语言:javascript
复制
function App() {
  const lastRenderTime = new Date().toString();
  
  const uecbFn = useEventCallback(
    () => alert("uecbFn: " + lastRenderTime),
  );

  return (
    <>
      Last render time: {lastRenderTime}
      <SomeChildComponent onClick={uecbFn} />
    </>
  );
}

每当<App>重新呈现时,就会创建一个新的箭头函数( useEventCallback自定义钩子的参数)。但是钩子内部只是将它存储在它的useRef当前占位符中。

钩子返回函数(在uecbFn中)永远不会改变。所以<SomeChildComponent>看不出任何变化。

但是恢复了最初预期的行为:在执行回调时,它将查找当前占位符内容,这是最近创建的箭头函数。因此,它使用了最近的lastRenderTime值!

<SomeChildComponent>实例

依赖于其回调支持之一的引用的组件的示例可以是:

代码语言:javascript
复制
function SomeChildComponent({
  onClick,
}: {
  onClick: () => void;
}) {
  const countRef = useRef(0);
  
  useEffect(
    () => { countRef.current += 1; },
    [onClick] // Increment count when onClick reference changes
  );
  
  return (
    <div>
      onClick changed {countRef.current} time(s)
      <button onClick={onClick}>click</button>
    </div>
  )
}

CodeSandbox上的演示:https://codesandbox.io/s/nice-dubinsky-qjp3gk

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73466390

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档