Quick Recipe for Creating a React Hooks

Quick Recipe for Creating a React Hooks

Around React 17, Hooks were introduced, providing extra functionality to pure dumb functions. These hooks were accepted readily. Almost every existing react-package is coming up with its own hooks that continue to offer the same experience as in Class-based Components, which is super cool.

In the documentation, you will find React only provided us with around 10 hooks in total, out of which 3-4 hooks are mainly used. But the catch is they allowed developers to create their own hooks as a combination of these default Hooks. All the react packages are using this flexibility only to provide an improved experience.

These self-made Hooks, or known as Custom Hook, are just normal javascript functions that follow the rules of Hook. The Rules are not very hard. Check yourself below:

  • A hook name should start with use.
  • A hook can be called in React Component or another Custom Hook
  • A hook should be not be called in any conditional statement, loop, or nested function.

Keeping these rules in mind, let's discuss the Hook creation recipe.

Recipe


I am gonna tell you the Hook Recipe along with an example. In the below steps, we will create a Debounce Hook that will give us a function that can help us in adding debounce functionality to other parts.

Debouce Function: A Function that waits for a specific interval before performing another execution.

Here is what we are aiming for:


function Groot() {
  const speak = useCallback(() => {
    console.log("I am Groot");
  },[]);
  const debounce = useDebounceHook();
  const debouncedSpeak = debounce(speak);

  return <>{/* Some JSX */}</>;
}

Now, we know what we are willing to create. So, to create a hook as per rules below is the structure:

function useDebounceHook(){
  /**
  * 
  *Some Debounce Hook Logic
  *
  */
}

For the logic section, we need to add the implementation. In this case, the logic for debounce function.

Below is a definition of a normal Debounce Function that will wait for 1 second after every execution.

function debounce(action) {
  let timeout = null;
  return (...args) => {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
    timeout = setTimeout(() => {
      timeout = null;
      action(...args);
    }, 1000);
  };
}

Now the question is how to use the same in our Custom Hook.

For this, we are going to take the help of React's useCallback Hook.


function useDebounceHook() {
  const debounceCreator = useCallback((action) => {
    let timeout = null;
    return (...args) => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      timeout = setTimeout(() => {
        timeout = null;
        action(...args);
      }, 1000);
    };
  },[]);
}

Now, as you can see, we have wrapped our Debounce Function in useCallback Hook.

Why useCallback?? Because we wanted to have an identical copy of the debounce creator Function across all renders, just to avoid creating a new creator-function on every render. So best suited Hook is useCallback Hook.

Note:

  • In this case, our debounce creator function is not dependent on any other value. It's a pure function. Hence we have an empty Dependency Array.

We are ready with our Debounce Hook logic, but we still can't use it as we haven't exposed our function to the environment outside the Hook.

It can be effortlessly done by just returning the debounce creator function.


function useDebounceHook() {
  const debounceCreator = useCallback((action) => {
    let timeout = null;
    return (...args) => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      timeout = setTimeout(() => {
        timeout = null;
        action(...args);
      }, 1000);
    };
  },[]);
  return debounceCreator;
}

And with this, our custom hook is ready.

The common perception is why we are creating a custom hook if we are writing just a useCallback Hook in it. Why can't we just write it in our Component?

Yes, we could have done that as well, but for scenarios where debounce is required in multiple Components, we would have written the same debounce definition in all the Component. Which would have caused redundancy.

And by creating a Custom Hook for Debounce, we have segregated the Debounce logic from the components. Whenever we need to refer or perform changes on Debounce Functionality, we can directly refer to this hook and avoid the headache of searching all the project files.

Bounus

Gonna share an updated version of this debounced hook with some extra features.

Feature of Current Implementation of Deboucnce Hook:

  • It provides a debounce creator function.

Here is an updated version of debounce hook:

function useDebounceHook(defaultTime = 1000) {
  const debounceCreator = useCallback((action,time = defaultTime) => {
    let timeout = null;
    return (...args) => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      timeout = setTimeout(() => {
        timeout = null;
        action(...args);
      }, time);
    };
  },[defaultTime]);
  return debounceCreator;
}

Feature of This updated Implementation of Deboucnce Hook:

  • It provides a debounce creator function.
  • You are having an option to change the default debounce time now.
  • You can change the debounce time of any individual function as well.

Since the debounceCreator function is not pure anymore if you are playing to use useRef hook instead of useCallback, it will fail here and won't update with changes.

Also, there is a possibility of having multiple hooks in our custom hook, which is totally fine. In our case, useCallback is what we needed.

Below is the working example of Debouce Hook Functionality. You can check that as well.

Wrap up

So here is my definition of Custom Hooks

Custom Hooks are the functions that segregate the logic from the components so that they can be reused, and their name starts with 'use'.

And here is my recipe of creating one:

  • Have a clear working logic
  • Have a list of inputs
  • Have a list of output
  • Wrap logic in useFunction, take input as arguments and return output as array or object.
  • And your hook is ready.