javascript - react : weird stale closure issue with `useRef`

标签 javascript reactjs

我正在写 useDebounce实用钩子(Hook)。

function debounce(fn, delay) {
  let timer = 0;
  return (...args) => {    
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}

function useDebounce(fn, delay) {
  const ref = React.useRef(fn);

  React.useLayoutEffect(() => {
    ref.current = fn;
  }, [fn]);

  return useMemo(() => debounce(ref.current, delay), [delay]);
}

我用 ref存储回调并使用 useLayoutEffect 更新它因此 API 的使用者不需要内存他们自己的回调。而且我想先发制人地回答我知道useMemo有效,我知道您可以备注回调,即 fn传入 useDebounce来自外部,但我不想给 API 的用户带来负担,所以我自己做了。
这是一个现场演示:https://codesandbox.io/s/closure-bug-xcvyd?file=/src/App.js
现在我要谴责的功能是
  const increment = () => {
    console.log(count);
    setCount(count + 1);
  };
所以我把它传给了useDebounce但似乎该函数以 count 上的陈旧关闭结束因为它只更新 count来自 0 -> 1然后在那之后,无论您单击按钮多少次,它都不再更新。
是的,我知道我可以写 setCount(c => c + 1);解决这个问题。
但令我困惑的是,如果我重写 useMemo(() => debounce(ref.current, delay), [delay]);return useMemo(() => debounce((...args) => ref.current(...args), delay), [ delay ]);然后这个问题会自动修复。
我似乎无法理解 (...args) => ref.current(...args)正在解决问题。

最佳答案

让我们一步一步来看看发生了什么。

  • 您正在放置 fn内部引用。
  • 您正在使用新值更新 ref
  • 您正在通过 fn去抖内部useMemo这就是错误所在。

  • 在下一次渲染时,您将再次更新 ref ,但内存功能根本不使用它。它记得对第一次通过的 fn 的引用只有当你的钩子(Hook)的用户改变延迟时,这才会改变。
    在固定示例中,使用箭头函数会发生以下情况:
  • 您正在放置 fn内部引用
  • 您正在使用新值更新 ref
  • 您正在内存关闭 ref 的函数并且会在每次调用时查看它,所以它会选择最新鲜的 fn来自引用的值(value)。

  • function useDebounce(fn, delay) {
      // storing function into ref
      const ref = React.useRef(fn);
      
      // updating function after memoization, and on each render when function changed
      React.useLayoutEffect(() => {
        ref.current = fn;
      }, [fn]);
    
      return useMemo(function() {
        // here you are referencing current `fn`
        // The very first `fn` that was passed into hook
        // ref don't play role here - you are passing `ref.current`
        let fnToDebounce = ref.current 
        return debounce(fnToDebounce, delay)
      }, [delay]);
    }
    
    function useDebounce(fn, delay) {
      // storing function into ref
      const ref = React.useRef(fn);
      
      // updating function after memoization, and on each render when function changed
      React.useLayoutEffect(() => {
        ref.current = fn;
      }, [fn]);
    
      return useMemo(function() {
        // Here  you are closuring reference to `ref` and `fnToDebounce` have no idea what is inside it.
        // When it would be called, it will get ref and only at that point will retrieve `ref.current`, that will be the latest `fn`
        let fnToDebounce = (...args) => ref.current(...args);
        return debounce(fnToDebounce, delay);
      }, [delay]);
    }
    

    关于javascript - react : weird stale closure issue with `useRef` ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66616061/

    相关文章:

    javascript - 为什么 event.target.value 只捕获字符串输入中的第一个字母

    javascript - 如何在 React Native 中监听触摸事件?

    node.js - 之后尝试启动 React 时出错(项目依赖树可能有问题....)

    seo - SEO 如何影响 ReactJS 页面变化

    javascript - 检查 JavaScript 对象中的多个键?

    javascript - 如何在没有移动设备的情况下模仿滑动

    javascript - 用 typescript 编写的 CLick 事件监听器自行解除绑定(bind)

    reactjs - 页面更改时 Redux 状态清除

    javascript - 将根域重定向到子域的影响

    javascript - ng-show/ng-hide 限制多次调用