Building a custom Hook

Building a custom Hook

ยท

3 min read

Today I have been working on custom hooks, and I'd like to share with you the key principles I have learnt today.

When building a custom hook, the file name doesn't matter. However, the function name must start with use to signal React that it is a custom hook, and it will guarantee React that this function will be used by respecting the rules of hooks.

Custom hooks can contain other hooks from react, and can be a great way to make our code simple to read and share the same piece of code/functionality across different components.

If you use the custom hook inside a component, its state will be tied to the component. Likewise, if we use the custom hook in multiple components, every component will receive its own state. So, we are not sharing a state by using the same hook across multiple components, but we are instantiating a new state for each component.

Let's see an example:

import {useEffect, useState} from "react";

const useCounter = () => {
    const [counter, setCounter] = useState(0);

    useEffect(() => {
        const interval = setInterval(() => {
            setCounter((prevCounter) => prevCounter + 1);
        }, 1000);

        return () => clearInterval(interval);
    }, []);

    return  counter
}

export default useCounter;

This custom hook allows me to create a counter that increments its value every second and returns the new value. We can then use this custom hook in a component by importing it:

import Card from './Card';
import useCounter from "../hooks/use-counter";

const ForwardCounter = () => {
  const counter = useCounter()

  return <Card>{counter}</Card>;
};

export default ForwardCounter;

Make it flexible

Now I want to configure my custom hook so that we can either use it to increment or decrement the counter output. This is very simple, we just need to pass some parameters to our useCounter function, same as with normal functions.

import {useEffect, useState} from "react";

const useCounter = (decrement = false) => {
    const [counter, setCounter] = useState(0);

    useEffect(() => {
        const interval = setInterval(() => {
            if (!decrement) {
                setCounter((prevCounter) => prevCounter + 1);
            } else {
                setCounter((prevCounter) => prevCounter - 1);
            }
        }, 1000);

        return () => clearInterval(interval);
    }, [decrement]);

    return counter
}

export default useCounter;

Please note, since the decrement parameter is not defined inside the useEffect function, neither is defined outside the custom hook. However, since it's a value that we get as a parameter, we have to add it as a dependency. Also, decrement has been set with a default value of false.

When passing a parameter to the custom hook, we are not limited to boolean or primitive values. We can also pass more complex functions:

import {useEffect, useState} from "react";

const useCounter = (customFn = false) => {
    const [counter, setCounter] = useState(0);

    useEffect(() => {
        const interval = setInterval(() => {
            if (customFn) {
                setCounter(customFn);
            } else {
                setCounter((prevCounter) => prevCounter + 1);
            }
        }, 1000);

        return () => clearInterval(interval);
    }, [customFn]);

    return counter
}

export default useCounter;

-----------------------------------------------------------


import Card from './Card';
import useCounter from "../hooks/use-counter";

const crazyCounter = () => {
  const counter = useCounter((prevValue)=> (prevValue + 1) * 2)

  return <Card>{counter}</Card>;
};

export default crazyCounter;

You can check out my code on GitHub ๐Ÿ‡ฎ๐Ÿ‡น๐Ÿ•๐Ÿˆโ€โฌ›

ย