Detalhes do pacote

use-debounce

xnimorz8mMIT10.0.5

Debounce hook for react

debounce, react-hook, react

readme (leia-me)

useDebounce, useDebouncedCallback & useThrottledCallback

React libraries for debouncing without tears!

  • Small size < 1 Kb
  • Compatible with underscore / lodash impl — learn once, use everywhere
  • Server-rendering friendly!

Features

Install

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

Copy paste guidance:

use-debounce

Simple usage: https://codesandbox.io/s/kx75xzyrq7

Debounce HTTP request: https://codesandbox.io/s/rr40wnropq

Debounce HTTP request with leading param: https://codesandbox.io/s/cache-example-with-areas-and-leading-param-119r3i

use-debounce callback

Simple usage: https://codesandbox.io/s/x0jvqrwyq

Combining with native event listeners: https://codesandbox.io/s/32yqlyo815

Cancelling, maxWait and memoization: https://codesandbox.io/s/4wvmp1xlw4

HTTP requests: https://codesandbox.io/s/use-debounce-callback-http-y1h3m6

Changelog

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

Simple values debouncing

According to https://twitter.com/dan_abramov/status/1060729512227467264 WebArchive link: https://web.archive.org/web/20210828073432/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 } 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);

The same API is available for useDebounce calls:

const [value, {cancel, isPending, flush}] = useDebounce(valueToDebounce);
...
cancel() // cancels pending debounce request
isPending() // returns if there is a pending debouncing request
flush() // immediately flushes pending request

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 } 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:

import React, { useCallback } from 'react';

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 [useDebounce ONLY] 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.

changelog (log de mudanças)

10.0.4

  • Fix behaviour for strictMode react when leading is set to true and trailing is true

10.0.3

10.0.2

10.0.1

  • Fixed flush method return args, thanks to @h

10.0.0

  • Major breaking change: replaced index.modern.js with index.mjs. Might require a little change in your build pipeline
  • Major breaking change: New option debounceOnServer: if you put the option to true, it will run debouncing on server (via setTimeout). The new option can break your current server behaviour, as v9.x, it runs all the time and might cause unnessesary server CPU utilisation. Now, by default, debounced callbacks do not happen on server.
  • Minor breaking change: Replaced useState for useDebounce with useReducer. It might lead to reduced amount of re-renders, as useState is known to have excess re-renders in some corner: https://stackoverflow.com/questions/57652176/react-hooks-usestate-setvalue-still-rerender-one-more-time-when-value-is-equal
  • Minor breaking change: useDebouncedCallback now updates function to call asap. Meaning, if you re-called the hook and it should trigger immediately, it will trigger the newest function all the time.
  • Lib size: 914 B: index.js.gz 851 B: index.js.br 883 B: index.mjs.gz 826 B: index.mjs.br 938 B: index.module.js.gz 873 B: index.module.js.br 989 B: index.umd.js.gz 919 B: index.umd.js.br
  • [Internal] Replaced Enzyme with @testing-library
  • [Internal] yarn classic => npm
  • [Internal] Updated devDependencies

9.0.4

9.0.3

  • Represent correct return type from useDebounce(), see issue, thanks to @appden

9.0.2

  • Reverted 9.0.0. We will revisit these changes later

9.0.0

  • Moved use-debounce to support modules see issue Thanks to @matewilk
  • breaking change The path to dist/index.js is changed. Now it's dist/index.cjs.

8.0.4

8.0.3

8.0.2

8.0.1

  • update library exports section to make exports work correctly with jest@28

8.0.0

  • breaking change useDebounce changed its build system to microbundle. For now we have several entries:

index.js is for commonJS approach index.modern.js for esnext module system index.umd.js for UMD. All the files are in dist folder.

If you have any paths which have esm or lib, please, replace them to dist:

Before:

import useDebounceCallback from 'use-debounce/lib/useDebounceCallback';

After:

import { useDebounceCallback } from 'use-debounce';

7.0.1

  • debounced object now is preserved for use-debounce between the renders. Thanks to @msharifi99 for reporting.

7.0.0

  • breaking change useDebounce hook changed isPending behavior from async reacting to the sync. Now isPending returns True as soon as the new value is sent to the hook.
  • Dev dependencies updated

6.0.1

6.0.0

  • breaking change: removed callback field, instead of this useDebouncedCallback and useThrottledCallback returns a callable function: Old:

    const { callback, pending } = useDebouncedCallback(/*...*/);
    // ...
    debounced.callback();
    

    New:

    const debounced = useDebouncedCallback(/*...*/);
    // ...
    debounced();
    /**
     * Also debounced has fields:
     * {
     *   cancel: () => void
     *   flush: () => void
     *   isPending: () => boolean
     * }
     * So you can call debounced.cancel(), debounced.flush(), debounced.isPending()
     */
    

    It makes easier to understand which cancel \ flush or isPending is called in case you have several debounced functions in your component

  • breaking change: Now useDebounce, useDebouncedCallback and useThrottledCallback has isPending method instead of pending

    Old:

    const { callback, pending } = useDebouncedCallback(/*...*/);
    

    New:

    const { isPending } = useDebouncedCallback(/*...*/);
    /**
     * {
     *   cancel: () => void
     *   flush: () => void
     *   isPending: () => boolean
     * }
     */
    
  • get rid of useCallback calls

  • improve internal typing

  • decrease the amount of functions to initialize each useDebouncedCallback call

  • reduce library size:

    Whole library: from 946 B to 899 B === 47 B useDebounce: from 844 to 791 === 53 B useDebouncedCallback: from 680 to 623 === 57 B useThrottledCallback: from 736 to 680 === 56 B

5.2.1

5.2.0

  • Added useThrottledCallback

5.1.0

wait param is optional. If you don't provide a wait argument, use-debounce will postpone a callback with requestAnimationFrame if it's in browser environment, or through setTimeout(..., 0) otherwise.

5.0.4

  • Add an export for React Native

5.0.3

5.0.2

  • Add size-limit and configure it for esm modules. Now the size of the whole library is limited within 1 KB (thanks to @omgovich)

  • Add an export map to your package.json. (thanks to @omgovich)

  • Reduce bundle size (thanks to @omgovich): Before:

    esm/index.js
    Size:       908 B with all dependencies, minified and gzipped
    
    esm/index.js
    Size:       873 B with all dependencies, minified and gzipped
    
    esm/index.js
    Size:       755 B with all dependencies, minified and gzipped
    

    Now:

    esm/index.js
    Size:       826 B with all dependencies, minified and gzipped
    
    esm/index.js
    Size:       790 B with all dependencies, minified and gzipped
    
    esm/index.js
    Size:       675 B with all dependencies, minified and gzipped
    
  • Add notes about returned value from debounced.callback and its subsequent calls: https://github.com/xnimorz/use-debounce#returned-value-from-debouncedcallback

  • Add project logo (thanks to @omgovich): use-debounce

5.0.1

  • Fix typing to infer correct callback type (thanks to @lytc)

5.0.0

  • breaking change: Now useDebouncedCallback returns an object instead of array:

    Old:

    const [debouncedCallback, cancelDebouncedCallback, callPending] =
      useDebouncedCallback(/*...*/);
    

    New:

    const debounced = useDebouncedCallback(/*...*/);
    /**
     * debounced: {
     *   callback: (...args: T) => unknown, which is debouncedCallback
     *   cancel: () => void, which is cancelDebouncedCallback
     *   flush: () => void, which is callPending
     *   pending: () => boolean, which is a new function
     * }
     */
    
  • breaking change: Now useDebounce returns an array of 2 fields instead of a plain array: Old:

    const [value, cancel, callPending] = useDebounce(/*...*/);
    

    New:

    const [value, fn] = useDebounce(/*...*/);
    /**
     * value is just a value without changes
     * But fn now is an object: {
     *   cancel: () => void, which is cancel
     *   flush: () => void, which is callPending
     *   pending: () => boolean, which is a new function
     * }
     */
    
  • Added pending function to both useDebounce and useDebouncedCallback which shows whether component has pending callbacks Example:

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

For more details of these major changes you could check this commit https://github.com/xnimorz/use-debounce/commit/1b4ac0432f7074248faafcfe6248df0be4bb4af0 and this issue https://github.com/xnimorz/use-debounce/issues/61

  • Fixed security alerts

4.0.0

  • breaking change: Support lodash style throttling options for trailing+maxWidth. Thanks to @tryggvigy Example:
useDebouncedCallback(callback, 300, {
  leading: true,
  trailing: false,
  maxWait: 300,
});

Where the trailing edge is turned off. Let's say the function is called twice in the first 300ms. Now debounced function to have been called once.

how to migrate: Please, check your traling: false params with maxWait option

  • breaking change: Now in case delay option is unset, it will be requestAnimationFrame delay

  • breaking change: Now debouncedCallback from useDebouncedCallback returns a value. In v3 it used to return undefined:

3.4.3

  • Fix use-debounce so that it works correctly with react-native and next.js (as both of them use fast-refresh).

3.4.2

  • Clear cache in build directory. Thanks to @wangcch

3.4.1

  • update types, so that they are more convinient. Thanks to @astj

3.4.0

  • Now callPendings wrapped with useCallback hook, so that the reference to the function would be the same. Thanks to @jfschwarz

3.3.0

3.2.0

3.1.0

  • Now package includes only nessesary files. Thanks to @vkrol
  • Added optional equalityFn to options object for useDebounce so that you can provide a custom equality function to the hook. Thanks to @seruco

3.0.1

  • Added missed esm directory (thanks for reporting @FredyC)
  • Fixed import name (thanks for PR @neoromantic)
  • Updated eslint-utils lib version due to security issue

3.0.0

  • breaking change now, cache file renamed to useDebounce and callback file renamed to useDebouncedCallback. If you used to import file by its path:
import useDebounce from 'use-debounce/lib/cache';
import useDebouncedCallback from 'use-debounce/lib/callback';

it should be renamed to

import useDebounce from 'use-debounce/lib/useDebounce';
import useDebouncedCallback from 'use-debounce/lib/useDebouncedCallback';

It helps us to keep more descriptive names. Thanks to @vkrol https://github.com/xnimorz/use-debounce/pull/33

2.2.1

— Added types field in package.json. Thanks to @nmussy

2.2.0

2.1.0

  • Rewrite to typescript

2.0.1

2.0.0

  • breaking changes now, useDebouncedCallback doesn't have deps argument. If you want to cache your callback it's better to use:
const myCallback = useDebouncedCallback(
  useCallback(() => {
    /* do some stuff */
  }, [value]),
  500
);
  • added size-limit to the project.
  • Reduce size of the library from 705 bytes to 352 bytes (50%)

1.1.3

  • remove react-dom from peerDependencies (as you can use this library with react native).

1.1.2

  • useCallback now memoize returned callback

1.1.0

  • add callPending callback to useDebouncedCallback 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/lib/callback';

function InputWhichFetchesSomeData({ defaultValue, asyncFetchData }) {
  const [debouncedFunction, cancel, callPending] = useDebouncedCallback(
    (value) => {
      asyncFetchData;
    },
    500,
    [],
    { maxWait: 2000 }
  );

  // When the component goes to be unmounted, we will fetch data if the input has changed.
  useEffect(
    () => () => {
      callPending();
    },
    []
  );

  return (
    <input
      defaultValue={defaultValue}
      onChange={(e) => debouncedFunction(e.target.value)}
    />
  );
}

More examples are available here: https://github.com/xnimorz/use-debounce/commit/989d6c0efb4eef080ed78330233186d7b0c249e3#diff-c7e0cfdec8acc174d3301ff43b986264R196

1.0.0

The example with all features you can see here: https://codesandbox.io/s/4wvmp1xlw4

  • add maxWait option. The maximum time func is allowed to be delayed before it's invoked:
import { useDebounce, useDebouncedCallback } from 'use-debounce';

...
const debouncedValue = useDebounce(value, 300, {maxWait: 1000});
const debouncedCallback = useDebouncedCallback(() => {...}, 300, [], {maxWait: 1000});
  • add cancel callback (thanks to @thibaultboursier for contributing). Cancel callback removes func from the queue (even maxWait):
import { useDebounce, useDebouncedCallback } from 'use-debounce';

...
const [ debouncedValue, cancelValueDebouncingCycle ] = useDebounce(value, 1000);
const [ debouncedCallback, cancelCallback ] = useDebouncedCallback(() => {...}, 1000);
  • [BREAKING] change the contact of use-debounce callback and value hooks:

Old:

import { useDebounce, useDebouncedCallback } from 'use-debounce';

...
const debouncedValue = useDebounce(value, 1000);
const debouncedCallback = useDebouncedCallback(() => {...}, 1000);

New:

import { useDebounce, useDebouncedCallback } from 'use-debounce';

...
const [ debouncedValue, cancelValueDebouncingCycle ] = useDebounce(value, 1000);
const [ debouncedCallback, cancelCallback ] = useDebouncedCallback(() => {...}, 1000);

You still can use only value and callback:

import { useDebounce, useDebouncedCallback } from 'use-debounce';

...
const [ debouncedValue ] = useDebounce(value, 1000);
const [ debouncedCallback ] = useDebouncedCallback(() => {...}, 1000);

0.0.x

  • add use-debounce callback and use-debounce value. First one is useful for debouncing callbacks e.g. event handlers, second one is handy for debouncing a value such as search fields etc.