Using & Building React Custom Hooks - A Developer's Guide

Dillion Megida
March 1, 2022
Try Memberstack for free

TABLE OF CONTENTS

Add memberships to your Webflow project in minutes.

Try Memberstack

Over 200 free cloneable Webflow components. No sign up needed.

View Library

Add memberships to your React project in minutes.

Try Memberstack

In this article, we’ll understand what React Custom Hooks are, the benefits of using them and when to use them. We’ll also learn how to build two custom hooks and show them in use.

What is a Custom Hook in React?

React Hooks allow you to hook into the state and lifecycle of a component in functional components. In previous versions of React, you could only have access to state and lifecycles in a class component. Hooks make it possible in functional components.

React has built-in hooks like useEffect, useState, and many more, which provide a lot of use cases. However, sometimes, you want a Custom Hook.

A custom hook allows you to create shared component logic that can be used in many components. These custom hooks will also use the built-in hooks but in one place. They’re like functions, prefixed with “use” (because they use in-built hooks and also follow the Rules of Hooks), which can accept any number of arguments and expose values and methods that can be used by a component.


Examples of Custom React Hook

Many custom React hooks are created and are still being developed for common logic in applications. Here are a few of them:

  1. use-mouse-action: for listening and handling up and down mouse action events
  2. use-clippy: for reading and writing to a user’s clipboard
  3. @rehooks/online-status: for subscribing to online/offline events
  4. useDebounce React Hook: for debouncing any fast-changing value
  5. useToggle React Hook: for toggling values to the opposite


Why & When you should use custom hooks in React

The primary benefit of writing and using a custom hook is that you don’t have to repeat the same logic in many places of your application. Repetition often breaks the DRY rule and makes your application more difficult to manage. Custom hooks are helpful utilities that help you reuse code logic.

The main difference between custom hooks and regular utility functions is that custom hooks usually involve the use of other in-built hooks and follow the rules of hooks, while the latter is a regular function that can be used anywhere. Let’s see an example:

Let’s say you want to keep track of a user’s network connection state. You keep track of an online state (using the useState hook) and update it to true or false when needed. You also create a listener in useEffect for the online and offline events to update the state accordingly.

If you’re doing all these logic—initializing state, checking network status based on an event, updating connected state—in one component, it’s okay. But this logic looks very common and can be used across your application. Your application will be more challenging to maintain if you repeat this logic in many more components. If you want to change how the connection status logic works, you’d have to update so many places of your applications. Hence, the need for code reusability.

When should you use custom cooks? Whenever you want to want to reuse logic across components. Custom hooks improve your code and your application in general. It creates a nice separation between the logic of your code and the view in the component.

Using a custom hook in React

Using a custom hook is straightforward, but it depends on the data that the hook exposes and the argument that it requires. If the hook exposes an object, you can extract values using the object keys like so:


const { key1, method1 } = useCustomHook()


If it exposes an array, just like useState, you can extract values using a different name:


const [online, setOnline] = useCustomHook()
const [number, setNumber] = useCustomHook()


Then you can do the same things in your application and can trigger the methods when needed.

One thing to note is that a custom hook doesn’t serve as a global state. This means you cannot share state across components. When you use a custom hook in a component, all state and data will be scoped to that component.

Building your own custom hook

Now we understand what hooks are, the benefit of using them and when we should. Moving further, we’ll learn how to build our own custom hook. We’ll be creating two hooks:

  • useOnlineStatus hook, which as explained earlier, will help us get access to the connection status of the user’s browser
  • useClickOutside hook, which will allows us to perform actions when the outside of a component is clicked

Firstly, let’s create our Project Folder using create-react-app:


npx create-react-app hooks-tutorial
cd hooks-tutorial

Create a hooks directory, src/hooks, and let’s add our hooks.

useOnlineStatus hook

In the hooks directory, create a file, useOnlineStatus.js with the following code:


import { useEffect, useState } from "react"

export default function useOnlineStatus() {
  const [online, setOnline] = useState(navigator.onLine)

  useEffect(() => {
    function handleStatusChange() {
      setOnline(navigator.onLine)
    }

    window.addEventListener("online", handleStatusChange)
    window.addEventListener("offline", handleStatusChange)

    return () => {
      window.removeEventListener("online", handleStatusChange)
      window.removeEventListener("offline", handleStatusChange)
    }
  }, [])

  return { online, setOnline }
}


Let me explain:

  • we’ve initialized the online state of the connection status
  • navigator.onLine returns true or false depending on the network connection status of the browser
  • the online and offline events are triggered on the document when the network connection status changes (for example, turning on/off your wifi)
  • and the handleStatusChange handler which updates the online state with the new status
  • we exposed an object with two properties: online and setOnline

With setOnline, a component can choose to manually update the online status. Also as you would notice here, we didn’t use any JSX. It’s a function which has access to React’s lifecyle using in-built hooks.

Now let’s see the hook in use. In App.js, import the hook:


// ..
import useOnlineStatus from "./hooks/useOnlineStatus"


Then use it:


function App() {  
  const { online, setOnline } = useOnlineStatus()  
  console.log("online", online)  

  // ..
}


Next, run the server, using npm run start and the website will be live on localhost:3000.

To test the hook, we’ll do few things:

  • open the browser console
  • refresh the website
  • turn off wifi
  • turn on wifi

And here’s the result:


useClickOutside hook

In the hooks directory, create a new file callec useClickOutside.js with the following code:


import { useEffect, useState } from "react"

export default function useClickOutside(ref) {
  const [isOutsideClicked, setIsOutsideClicked] = useState(false)

  const handleClickOutside = (event) => {
    if (ref.current && !ref.current.contains(event.target)) {
      setIsOutsideClicked(true)
    }
  }

  useEffect(() => {
    document.addEventListener("mousedown", handleClickOutside)
    return () => {
      document.removeEventListener("mousedown", handleClickOutside)
    }
  }, [])

  return [isOutsideClicked, setIsOutsideClicked]
}


Let’s see what’s happening here:

  • we create the hook function which accepts a ref argument
  • we initialize the isClicked state to false
  • we create a mousedown event listener
  • the mousedown event handler checks if the referenced element contains the current element clicked, and if true, updates the isClicked state.
  • then we expose the value and method from the hook

Now, let’s see how it’s used in App.js:


// ..
import useClickOutside from "./hooks/useClickOutside"
import React, { useRef } from "react"

function App() {
  const ref = useRef()
  const [isDivOutsideClicked, setIsDivOutsideClicked] = useClickOutside(ref)
  console.log("div is clicked outside: ", isDivOutsideClicked)

  return (
    <div className="App">
      <div
        ref={ref}
        style={{ width: "50px", height: "50px", backgroundColor: "yellow" }}
      ></div>
      // ..
    </div>
  )
}


Since we exported the data with an array, we can rename the values during destructuring. So instead of isOutsideClicked, we used isDivOutsideClicked, which makes it even more specific to the target element.

Back in the browser, when we click outside the yellow box, we can see the console showing that outside is clicked.



You can find the complete codebase in this repository: https://github.com/dillionmegida/custom-hooks-examples

Wrap up

Using custom hooks, we’ve seen how common logic in our applications can be abstracted into hooks. This helps to improve our code and make our application maintainable.

In this piece, we’ve seen the relevance of custom hooks, when to use them, and also gone through two tutorials showing how to build them.