Creating Custom React Hooks

Jas Spencer
4 min readMar 12, 2021

As noted in a previous post, hooks are a great part of the React library. They give extra functionality to functional components and can make life easier for the programmer and therefore the user. Hooks like useState and useEffect are some of the most common ones, but hooks don’t stop there. React lets you create your very own custom hooks to fit whatever needs your application has. Custom hooks are great because they take full advantage of the component based blocks of React and the ability to pass information and logic back and forth. Today we’re going to be looking at how to create custom React hooks.

As an example, let’s take a look at a simple react component where a use inputs their name in an input field.

import React, { useState } from 'react';export default function App() {
const [name, setName] = useState('')
return (
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
)
}

This isn’t that complicated, the initial state is an empty string, and when the name gets typed into the input field, the useState hook updates the state with the new name. However on a page refresh the inout field will be wiped clean as there is now way to persist the updated state via something like Local Storage. This presents an opportunity for a custom hook.

When creating a custom hook, is needs to begin with the word ‘use’ so that React can work its magic behind the scenes. In this example as we are hoping to work with Local Storage, let’s call the name of the file where we will create the hook ‘useLocalStorage.js’.

We are exporting a function containing the logic for the hook so a boiler plate would look like:

export default function useLocalStorage() {}

As we are using this hook to return some form of state, we can use some of the functionality of the useState hook inside our custom hook and also set up the initial state:

import React, { useState } from 'react';export default function useLocalStorage() {
const [value, setValue] = useState()
return [value, setValue]}

Now the return value of useLocalStorage is exactly the same as useState in the very first example above.

We can then bring in the new hook and replace the useState hook:

import React from 'react';
import useLocalStorage from './useLocalStorage'
export default function App() {
const [name, setName] = useLocalStorage('')
return (
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
)
}

We also need to determine an initialValue property in useLocalStorage:

import React, { useState } from 'react';export default function useLocalStorage(initialValue) {
const [value, setValue] = useState(initialValue)
return [value, setValue]}

At this point we have a custom hook that has useState wrapped up inside of it, however it’s not very useful as it doesn’t use Local Storage …yet!

The next step is to make sure our data persists and can be retrieved through local storage:

import React, { useState } from 'react';function getSavedValue(key, initialValue) //the key will be how it is stored in Local Storageexport default function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(initialValue)
return [value, setValue]}

Also updating the App component step by step:

import React from 'react';
import useLocalStorage from './useLocalStorage'
export default function App() {
const [name, setName] = useLocalStorage('name', '')
return (
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
)
}

Our ‘key’ property will have the value of ‘name’ in Local Storage, let’s keep building out the function:

import React, { useState } from 'react';function getSavedValue(key, initialValue) {
const savedValue = JSON.parse(localStorage.getItem(key)
// using the JSON.parse function converts the item to JSON
if(savedValue) return savedValue //finds out if something has already been saved return initialValue
}
export default function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(initialValue)
return [value, setValue]}

If we wanted this to work the same as useState we will need to change the inout of useState to a function and check to see if initial is an instance of a function:

import React, { useState } from 'react';function getSavedValue(key, initialValue) {
const savedValue = JSON.parse(localStorage.getItem(key)
if(savedValue) return savedValue if (initialValue instanceof Function)return initialValue() return initialValue
}
export default function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
return getSavedValue(key, initialValue)
})return [value, setValue]}

Changing the input of useState to a function results in it only running once upon the component rendering.

Now we need to write the code to update and save the value to Local Storage and the best way to do that is with useEffect.

import React, { useState, useEffect } from 'react';function getSavedValue(key, initialValue) {
const savedValue = JSON.parse(localStorage.getItem(key)
if(savedValue) return savedValueif (initialValue instanceof Function)return initialValue()return initialValue
}
export default function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
return getSavedValue(key, initialValue)
})useEffect(() => {
localStorage.getItem(key, JSON.stringify(value)) //only strings can be passed to Local Storage
}, [value]) //hook runs when value changes
return [value, setValue]}

Now our input value can be passed into Local Storage and retrieved as well!

While the above code might be complicated, keep in mind that all we have to do in order to use our extracted logic is simply import useLocalStorage and set it up the same way we would for useState or useEffect. That’s the great thing about custom hooks, once all the logic is completed it’s extremely simple to use in any component you want.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Jas Spencer
Jas Spencer

Written by Jas Spencer

Software Engineer in NYC with expertise in Ruby on Rails, JavaScript, and React

No responses yet

Write a response