如果我直接调用包含 setTimeout 逻辑的函数,则反应中的去抖动不起作用

Debouncing in react is not working if I directly call the function containing logic for setTimeout

我试图在 React 中的 useEffect 中实现去抖动功能。 尽管对于下面的代码,如果我调用 debounceFunc 它只执行一次,而如果我直接调用 debounceFunc3 它不起作用。

const handleClick = () => {
    setEvent((pre) => [...pre, 1]);
  };
  const delayFunc = () => {
    let timer;
    return function () {
      clearTimeout(timer);
      timer = setTimeout(() => console.log("Hello timer 1"), 2000);
    };
  };

  const delayFunc3 = useCallback(() => {
    let timer3;
    return function () {
      clearTimeout(timer3);
      timer3 = setTimeout(() => console.log("Hello timeout3"), 2000);
    };
  }, []);

  const debounce = useCallback(delayFunc(), []);

  useEffect(() => {
    if (event?.length > 0) {
      debounce();
      delayFunc3()();
    }
  }, [event]); 

在控制台中 Hello timer 1 仅打印一次,而 Hello timeout3 打印或每个事件 triggered/changed.

代码框 link 玩转:

https://codesandbox.io/s/inspiring-lamport-sbbup?file=/src/App.js:0-872

无法理解我打电话的方式有什么问题debounceFunc3

您的代码的问题是您多次执行 delayFunc3()

delayFunc3()();

问题在于,每次调用 delayFunc3()() 时,您都会在 delayFunc3 函数中创建一个新的本地 timer3 变量,然后执行现在返回的函数引用新的本地 timer3 变量。

这意味着如果你调用delayFunc3()();,首先创建一个新的timer3变量,然后执行返回的函数,排队一个新的超时。

如果您随后再次调用 delayFunc3()();,则会创建一个新的 timer3 变量(初始化为 undefined),然后执行您返回的函数。当您返回的函数被执行时,您正在运行 clearTimeout(timer3);,这不会清除任何计时器,因为 timer3 这里指的是已初始化的新 timer3 变量 [=21] =],而不是在上一个函数 delayFunc3()() 调用中创建的前一个 timer3

因此,在您的第一次超时仍为 运行 时再次调用 delayFunc3()(); 不会对第一次超时产生任何影响,因为此调用创建的 timer3 没有与之前创建的 timer3 相关,因此不会被清除。

与此不同,执行以下操作:

const debounce = useCallback(delayFunc(), []);

执行你的delayFunc()并在delayFunc()的范围内创建一个新的局部变量timer。从 delayFunc() closes over timer 范围内声明的 delayFunc() 变量返回的函数。这意味着无论何时执行 debounce(),您都不会像在第一个示例中那样创建新的 timer 变量,而是重复使用从初始 delayFunc() 调用。每次调用 debounce() 都会重用相同的 timer 变量,因此,如果再次调用 debounce()clearTimeout(timer); 将清除先前排队的计时器。


另请注意,在您的两个示例中,您都在两个不同的事物上使用 useCallback()。使用 debounce,您将对 delayFunc() 返回的函数调用 useCallback()。这意味着您组件的所有未来重新呈现都将使用在您的组件安装时从初始 delayFunc() 调用返回的一个函数引用。所以 debounce 指的是所有重新渲染的唯一功能。

与此不同,delayFunc3 不是在返回的函数上使用 useCallback(),而是在整个函数(即:“父”箭头函数)上使用。这意味着您的组件的每次重新渲染都将使用相同的“父”函数,但该父函数在调用时仍会创建一个新的 timer3 变量,并且每次调用时 returns 都会创建一个新的内部函数。