❤Neutrinos Peludos Magnéticos
    Have ideas to improve npm?Join in the discussion! »

    use-debounce
    TypeScript icon, indicating that this package has built-in type declarations

    6.0.1 • Public • Published

    Features

    Install

    yarn add use-debounce
    # or
    npm i use-debounce --save

    Demos

    The simplest way to start playing around with use-debounce is with this CodeSandbox snippet: https://codesandbox.io/s/kx75xzyrq7

    More complex example with searching for matching countries using debounced input: https://codesandbox.io/s/rr40wnropq (thanks to https://twitter.com/ZephDavies)

    Changelog

    https://github.com/xnimorz/use-debounce/blob/master/CHANGELOG.md

    Simple values debouncing

    According to https://twitter.com/dan_abramov/status/1060729512227467264

    import React, { useState } from 'react';
    import { useDebounce } from 'use-debounce';
    
    export default function Input() {
      const [text, setText] = useState('Hello');
      const [value] = useDebounce(text, 1000);
    
      return (
        <div>
          <input
            defaultValue={'Hello'}
            onChange={(e) => {
              setText(e.target.value);
            }}
          />
          <p>Actual value: {text}</p>
          <p>Debounce value: {value}</p>
        </div>
      );
    }

    This hook compares prev and next value using shallow equal. It means, setting an object {} will trigger debounce timer. If you have to compare objects (https://github.com/xnimorz/use-debounce/issues/27#issuecomment-496828063), you can use useDebouncedCallback, that is explained below:

    Debounced callbacks

    Besides useDebounce for values you can debounce callbacks, that is the more commonly understood kind of debouncing. Example with Input (and react callbacks): https://codesandbox.io/s/x0jvqrwyq

    import { useDebouncedCallback } from 'use-debounce';
    
    function Input({ defaultValue }) {
      const [value, setValue] = useState(defaultValue);
      // Debounce callback
      const debounced = useDebouncedCallback(
        // function
        (value) => {
          setValue(value);
        },
        // delay in ms
        1000
      );
    
      // you should use `e => debounced(e.target.value)` as react works with synthetic events
      return (
        <div>
          <input defaultValue={defaultValue} onChange={(e) => debounced(e.target.value)} />
          <p>Debounced value: {value}</p>
        </div>
      );
    }

    Example with Scroll (and native event listeners): https://codesandbox.io/s/32yqlyo815

    function ScrolledComponent() {
      // just a counter to show, that there are no any unnessesary updates
      const updatedCount = useRef(0);
      updatedCount.current++;
    
      const [position, setPosition] = useState(window.pageYOffset);
    
      // Debounce callback
      const debounced = useDebouncedCallback(
        // function
        () => {
          setPosition(window.pageYOffset);
        },
        // delay in ms
        800
      );
    
      useEffect(() => {
        const unsubscribe = subscribe(window, 'scroll', debounced);
        return () => {
          unsubscribe();
        };
      }, []);
    
      return (
        <div style={{ height: 10000 }}>
          <div style={{ position: 'fixed', top: 0, left: 0 }}>
            <p>Debounced top position: {position}</p>
            <p>Component rerendered {updatedCount.current} times</p>
          </div>
        </div>
      );
    }

    Returned value from debounced()

    Subsequent calls to the debounced function debounced return the result of the last func invocation. Note, that if there are no previous invocations it's mean you will get undefined. You should check it in your code properly.

    Example:

    it('Subsequent calls to the debounced function `debounced` return the result of the last func invocation.', () => {
      const callback = jest.fn(() => 42);
    
      let callbackCache;
      function Component() {
        const debounced = useDebouncedCallback(callback, 1000);
        callbackCache = debounced;
        return null;
      }
      Enzyme.mount(<Component />);
    
      const result = callbackCache();
      expect(callback.mock.calls.length).toBe(0);
      expect(result).toBeUndefined();
    
      act(() => {
        jest.runAllTimers();
      });
      expect(callback.mock.calls.length).toBe(1);
      const subsequentResult = callbackCache();
    
      expect(callback.mock.calls.length).toBe(1);
      expect(subsequentResult).toBe(42);
    });

    Advanced usage

    Cancel, maxWait and memoization

    1. Both useDebounce and useDebouncedCallback works with maxWait option. This params describes the maximum time func is allowed to be delayed before it's invoked.
    2. You can cancel debounce cycle, by calling cancel callback

    The full example you can see here https://codesandbox.io/s/4wvmp1xlw4

    import React, { useState, useCallback } from 'react';
    import ReactDOM from 'react-dom';
    import { useDebouncedCallback } from 'use-debounce';
    
    function Input({ defaultValue }) {
      const [value, setValue] = useState(defaultValue);
      const debounced = useDebouncedCallback(
        (value) => {
          setValue(value);
        },
        500,
        // The maximum time func is allowed to be delayed before it's invoked:
        { maxWait: 2000 }
      );
    
      // you should use `e => debounced(e.target.value)` as react works with synthetic events
      return (
        <div>
          <input defaultValue={defaultValue} onChange={(e) => debounced(e.target.value)} />
          <p>Debounced value: {value}</p>
          <button onClick={debounced.cancel}>Cancel Debounce cycle</button>
        </div>
      );
    }
    
    const rootElement = document.getElementById('root');
    ReactDOM.render(<Input defaultValue="Hello world" />, rootElement);

    Flush method

    useDebouncedCallback has flush method. It allows to call the callback manually if it hasn't fired yet. This method is handy to use when the user takes an action that would cause the component to unmount, but you need to execute the callback.

    import React, { useState, useCallback } from 'react';
    import { useDebouncedCallback } from 'use-debounce';
    
    function InputWhichFetchesSomeData({ defaultValue, asyncFetchData }) {
      const debounced = useDebouncedCallback(
        (value) => {
          asyncFetchData;
        },
        500,
        { maxWait: 2000 }
      );
    
      // When the component goes to be unmounted, we will fetch data if the input has changed.
      useEffect(
        () => () => {
          debounced.flush();
        },
        [debounced]
      );
    
      return <input defaultValue={defaultValue} onChange={(e) => debounced(e.target.value)} />;
    }

    isPending method

    isPending method shows whether component has pending callbacks. Works for both useDebounce and useDebouncedCallback:

    function Component({ text }) {
      const debounced = useDebouncedCallback(useCallback(() => {}, []), 500);
    
      expect(debounced.isPending()).toBeFalsy();
      debounced();
      expect(debounced.isPending()).toBeTruthy();
      debounced.flush();
      expect(debounced.isPending()).toBeFalsy();
    
      return <span>{text}</span>;
    }

    leading/trailing calls

    Both useDebounce and useDebouncedCallback work with the leading and trailing options. leading param will execute the function once immediately when called. Subsequent calls will be debounced until the timeout expires. trailing option controls whenever to call the callback after timeout again.

    For more information on how leading debounce calls work see: https://lodash.com/docs/#debounce

    import React, { useState } from 'react';
    import { useDebounce } from 'use-debounce';
    
    export default function Input() {
      const [text, setText] = useState('Hello');
      const [value] = useDebounce(text, 1000, { leading: true });
    
      // value is updated immediately when text changes the first time,
      // but all subsequent changes are debounced.
      return (
        <div>
          <input
            defaultValue={'Hello'}
            onChange={(e) => {
              setText(e.target.value);
            }}
          />
          <p>Actual value: {text}</p>
          <p>Debounce value: {value}</p>
        </div>
      );
    }

    Options:

    You can provide additional options as a third argument to both useDebounce and useDebouncedCallback:

    option default Description Example
    maxWait - Describes the maximum time func is allowed to be delayed before it's invoked https://github.com/xnimorz/use-debounce#cancel-maxwait-and-memoization
    leading - This param will execute the function once immediately when called. Subsequent calls will be debounced until the timeout expires. https://github.com/xnimorz/use-debounce#leading-calls
    trailing true This param executes the function after timeout. https://github.com/xnimorz/use-debounce#leading-calls
    equalityFn (prev, next) => prev === next Comparator function which shows if timeout should be started

    useThrottledCallback

    You are able to use throttled callback with this library also (starting 5.2.0 version). For this purpose use:

    import useThrottledCallback from 'use-debounce/useThrottledCallback';
    

    or

    import { useThrottledCallback } from 'use-debounce';
    

    Several examples:

    1. Avoid excessively updating the position while scrolling.

      const scrollHandler = useThrottledCallback(updatePosition, 100);
      window.addEventListener('scroll', scrollHandler);
    2. Invoke renewToken when the click event is fired, but not more than once every 5 minutes.

      const throttled = useThrottledCallback(renewToken, 300000, { 'trailing': false })
      <button onClick={throttled}>click</button>

    All the params for useThrottledCallback are the same as for useDebouncedCallback except maxWait option. As it's not needed for throttle callbacks.

    Special thanks:

    @tryggvigy — for managing lots of new features of the library like trailing and leading params, throttle callback, etc;

    @omgovich — for reducing bundle size.

    Install

    npm i use-debounce

    DownloadsWeekly Downloads

    395,800

    Version

    6.0.1

    License

    MIT

    Unpacked Size

    69.1 kB

    Total Files

    20

    Last publish

    Collaborators

    • avatar