xnimorz/use-debounce

useThrottledCallback time interval bug

Closed this issue · 1 comments

Describe the bug
time interval is not work as expected

To Reproduce

import { Button } from "@fluentui/react-components";
import { useEffect, useRef, useState } from "react";
import { useThrottledCallback } from "use-debounce";

export function AvatarPersona() {
  const [deleteQueue, setDeleteQueue] = useState<number[]>([]);
  const deleteLength = useRef(0);

  const testHookThrottledCallback = useThrottledCallback(() => {
    console.log(deleteQueue[0] + ": " + Date.now());
    setDeleteQueue(orgQ => {
      let curQ = orgQ.slice();
      curQ.splice(0, 1);
      return curQ;
    });
  }, 200);

  useEffect(() => {
    if (deleteQueue?.length > 0) {
      testHookThrottledCallback();
    }
  }, [deleteQueue])


  return (<>
    <Button onClick={() => {
      for (let i = 0; i < 10; i++) {
        setDeleteQueue((orgQueue) => {
          let curQ = orgQueue.slice();
          curQ.push(i);
          return curQ;
        });
        deleteLength.current++;
      }
    }}>def</Button>
  </>);
}

log result :
屏幕截图 2024-09-12 152950

Expected behavior
The last three digits of each timestamp should be incremented by 200 in turn.

use-debounce version:^10.0.3

Hello @GH-YinXiaoMin

Thank you for opening the issue.

Please, see the documentation of useThrottledCallback. The case which you describes requires leading param to be set to false, while the default value is true.

You can play with that param on sandbox I prepared:
https://codesandbox.io/p/sandbox/t84932?file=%2Fsrc%2Findex.js%3A28%2C8

import { useEffect, useRef, useState } from "react";
import { useThrottledCallback } from "use-debounce";

import { createRoot } from "react-dom/client";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(<AvatarPersona />);
let time;
export function AvatarPersona() {
  const [deleteQueue, setDeleteQueue] = useState([]);
  const deleteLength = useRef(0);

  const testHookThrottledCallback = useThrottledCallback(
    () => {
      const newTime = Date.now();
      console.log(deleteQueue[0] + ": " + (newTime - time));
      time = newTime;
      setDeleteQueue((orgQ) => {
        let curQ = orgQ.slice();
        curQ.splice(0, 1);
        return curQ;
      });
    },
    200,
    // By default, throttling uses leading = true,
    // which means, the very first call is executed immediately
    { leading: false }
  );

  useEffect(() => {
    if (deleteQueue?.length > 0) {
      testHookThrottledCallback();
    }
  }, [deleteQueue]);

  return (
    <>
      <button
        onClick={() => {
          time = Date.now();
          for (let i = 0; i < 10; i++) {
            setDeleteQueue((orgQueue) => {
              let curQ = orgQueue.slice();
              curQ.push(i);
              return curQ;
            });
            deleteLength.current++;
          }
        }}
      >
        def
      </button>
    </>
  );
}

In that example, usage of leading: false results in 200ms delay between any subsequent calls
image

While removing leading: false will make the opposite