DEV Community

Christian Hansen
Christian Hansen

Posted on

Basics of making custom hooks in React

Why use hooks?

Hooks are a new feature in React. They are an excellent way to share stateful logic between components. They are also incredibly composable which fits in great with React since React is all about composition.

Please see the hooks documentation for more information on the basics of hooks. I will also list some other great resources at the end of this post.

Rules to be considered a custom hook

  1. The name of the custom hook needs to start with use like useState.
  2. The custom hook can call other hooks.
  3. The custom hook needs to follow the rules of using hooks, this being only calling hooks from the top level of the function only. You can not call hooks from within conditionals, loops, or nested functions.

Basic example

Here is a simple and trivial example to get us started. This is a custom hook called useCounter. A user of this hook can create a counter with ease by passing in the initial count and then using the count and functions returned.

I first have the use of the custom hook in a Counter component. All I have to do is invoke it and I get the state and functions I need.

import React from 'react'
import useCounter from './useCounter'

const Counter = ({initialCount}) => {
    // here is the invocation of useCounter
    // I can pass in the initial count
    // It returns to me the count as well as two functions to help me change it
    const { count, increment, decrement } = useCounter(initialCount) 

    return (
        <div>
            <button onClick={increment}>Increment</button>
            <h1>{count}</h1>
            <button onClick={decrement}>Decrement</button>
        </div>
    )
}

Here is the implementation of useCounter. It follows the rules as stated above. It begins with use and calls other hooks from within it. The other hooks are called at the top level. I could have easily included this within the Counter component, but it is super useful to be able to extract the logic and state into a custom hook when logic gets complicated or needs to be reused.

import React from 'react'

const useCounter = initial => {
    const [count, setCount] = React.useState(initial)
    const increment = () => {
        setCount(c => c + 1)
    }

    const decrement = () => {
        setCount(c => c - 1)
    }

    return {
        count,
        increment,
        decrement,
    }
}

Here is another example. This one uses useEffect as well as useState. This hook could be imported anywhere you need a clock in your application. You would only need to invoke it and then clock would always hold the current local time.

import React from 'react'

const useClock = () => {
    const [clock, setClock] = React.useState(new Date().toLocaleTimeString())

    React.useEffect(() => {
        let intervalId = setInterval(() => {
            setClock(new Date().toLocaleTimeString())
        }, 1000)
        return () => {
            clearInterval(intervalId)
        }
    }, [])

    return {
        clock,
    }
}

Hooks composition

Thus far in this article, I have shown custom hooks that use the base hooks of useState and useEffect which React provides. Hooks can call other custom hooks as well! This leads to a powerful composition of hooks pattern.

Below is an example of using a custom hook within another custom hook. It easily could have been implemented in one hook, but hopefully it demonstrates composing them.

import React from 'react'

const useIsEmpty = () => {
    const [count, setCount] = React.useState(0)
    const empty = React.useMemo(() => count === 0, [count])

    const increment = () => {
        setCount(x => x + 1)
    }
    const decrement = () => {
        setCount(x => x - 1)
    }

    return {
        empty,
        increment,
        decrement,
    }
}

const useList = () => {
    const [list, setList] = React.useState([])
    const {empty, increment, decrement} = useIsEmpty()
    const addToEnd = (newItem) => {
        setList(l => [...l, newItem])
        increment()
    }
    const removeLast = () => {
        setList(l => [...l.slice(0, l.length)])
        decrement()
    }

    return {
        list,
        addToEnd,
        removeLast,
        empty
    }
}

Try out hooks today!

See what you can do with hook. Try implementing something you would normally do in React but with hooks.

  • Check out this list of hooks to see what others are doing.
  • Check out this great post by Tanner Linsley on hooks
  • Also have a look at this post by Dan Abramov. He has been posting lots of awesome content and people have been helping to translate it into many languages!

Thanks for reading!

Latest comments (1)

Collapse
 
httpjunkie profile image
Eric Bishard

Hi Christian,

Tanner's link is not working. I got some good resources I would recommend considering the article topic if you would like.