DEV Community

Cover image for Custom React hook : useDebouncedEffect()
TusharShahi
TusharShahi

Posted on

5

Custom React hook : useDebouncedEffect()

Background

Recently I saw a question on Stackoverflow where the poster was looking to run a useEffect in debounced manner.

Here is a screenshot:

The original question

This makes sense as many times as there are situations where some effects need to run but not on every change to a dependency. Eg: In a type search bar which makes API calls, we can run useEffect in a debounced manner to optimise resource utilisation.

I tried to have a go at the answer and tried making a custom hook to solve the problem. I will try to summarise the hook below:

The hook

This is the basic structure we know our custom hook would follow:



const useDebounceEffect = (fnc, deps, delay) => {

 useEffect(() => {


 },deps);

}


Enter fullscreen mode Exit fullscreen mode

In addition to the params of the standard useEffect, this will have a delay parameter too. The hook internally will still use useEffect.

We will need to use a setTimeout to schedule our callback function. This timeout needs to be cleared too, so it is important to keep the ID of the timeout. We need to have a ref which holds our timeout ID. State variable is not required here since we do not want to cause a re-render whenever our ID changes.



const useDebounceEffect = (fnc, deps, delay) => {
  const ref = useRef();

  useEffect(() => {
    clearTimeout(ref.current);
    ref.current = setTimeout(() => {
      fnc();
      clearTimeout(ref.current);
    }, delay);
  }, deps);
};


Enter fullscreen mode Exit fullscreen mode

The hook looks almost ready, but there is one problem:
The dependencies are not complete. If after a render, fnc changes or delay changes, the effect is not triggered.

We get a warning for that from our linter too and we fix it to complete the hook.



const useDebounceEffect = (fnc, deps, delay) => {
  const ref = useRef();

  useEffect(() => {
    clearTimeout(ref.current);
    ref.current = setTimeout(() => {
      fnc();
      clearTimeout(ref.current);
    }, delay);
  }, [fnc, ...deps,delay]);
};


Enter fullscreen mode Exit fullscreen mode

Note how we do not need to mention ref in the dependency. ref.current will always have the correct value since it is a mutable container and changing ref.current does not cause a rerender.

Links

With this our hook is complete. Here is link to the original question and a running demo

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️