How to create your own custom Hooks in React (extensive guide)
Let's understand creating React custom Hooks with examples.
Table of contents
Introduction
What are custom Hooks
Hooks are reusable functions.
When you have component logic that needs to be used by multiple components, we can extract that logic to a custom Hook.
Custom Hooks start with "use". Example: useFetch.
use
should be a must.
Why and when to use custom React Hooks
Custom hooks are an essential part of React. As a React developer, it is essential for you to learn to create custom Hooks to solve problems or to add extra features to your React project.
The main reason to write a custom Hook is code reusability. For example, instead of writing the same code again and again in different components that use the same logic, you can write that code inside a custom Hook and reuse it.
Rules of Hooks
The general rules of Hooks also applied to custom Hooks.
- Only call Hooks at top levels.
- Only call Hooks from React functions.
- Don't call Hooks from regular JavaScript functions.
Creating a custom Hook
useFetch
Suppose our custom Hook name is useFetch. Let's say we are calling an API and fetching data. Then show the title from that data to the page.
Scenario without custom Hook
import { useState, useEffect } from "react";
const Home = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos")
.then((res) => res.json())
.then((data) => setData(data));
}, []);
return (
<>
{data &&
data.map((item) => {
return <p key={item.id}>{item.title}</p>;
})}
</>
);
};
Scenario with custom Hook
We may need the fetch logic in other components as well. So we can extract it out in a custom Hook.
import useFetch from "./useFetch";
const Home = () => {
const [data] = useFetch("https://jsonplaceholder.typicode.com/todos");
return (
<>
{data &&
data.map((item) => {
return <p key={item.id}>{item.title}</p>;
})}
</>
);
};
Here we can see that we are passing the API in useFetch Hook. And it is returning us the data. Which we are using further. Below is the logic for that Hook, that we have extracted from API part.
Custom Hook logic
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => setData(data));
}, [url]);
return [data];
};
The logic goes as follows:
use
keyword is used before the function name to make react understand that it is a Hook.- Our own custom Hook is also using Hooks provided by React.
- After using API fetch logic, we return state
data
- That's it. 😲
Let's move to another example
useLocalStorage
Let's say our custom Hook name is useLocalStorage
. Suppose we are making a counter app. When we click on the button counter value will be added to local storage. And then we will get value from localStorage and show to our page.
Scenario without custom Hook
function Counter(){
const [counter, setCounter] = useState(()=>{
let value;
try{
value = JSON.parse(
window.localStorage.getItem('my-count') || '0');
}
catch(e){
value = 0;
}
return value;
});
useEffect(()=>{
window.localStorage.setItem('my-count', count);
},[count]);
return (
<div>
<button onClick={()=> setCount(count + 1)}>{count}
</button>
</div>
)
}
Working of the code
- getting value from
localStorage
, if it's not present setting it to 0. - On every click update the counter value in
localStorage
Now, what if you have to build another counter app and increase that counter value by 5. Now for that, you have to write the same logic again. That's not a good idea to write the same logic again. Let's wrap it up into a custom Hook and use that.
Scenario with custom Hook
function Counter(){
const [counter, setCounter] = useLocalStorage('my-count',0);
return (
<div>
<button onClick={()=> setCount(count + 1)}>{count}</button>
</div>
)
}
Here we can see that we have used our localStorage Hook.
And passed the key and initial value for localStorage. And it is returning us state
and setState
.
Custom Hook logic
function useLocalStorage(key, defaultValue){
const [state, setState] = useState(()=>{
let value;
try{
value = JSON.parse(
window.localStorage.getItem(key) || String(defaultValue));
}
catch(e){
value = defaultValue;
}
return value;
});
useEffect(()=>{
window.localStorage.setItem(key, state);
},[state]);
return [state, setState]
}
We have extracted out the localStorage logic from our code and put into the custom Hook. The logic goes as follows:
use
keyword used before function name.- Here we are taking key and defaultValue are parameters.
- Here we have used useState Hook.
- After all things, in the end we are returning
[state, setState]
.
Now, this looks better. Wherever we need localStorage logic we can use our own Hook. We can give any name for key and any default value while calling our Hook.
Cool facts
Have you noticed, that we returned data in an array and not as an object. But why? Because
If a Hook returns an array ([x]), then you are able to name the variables yourself.
If a Hook returns an object ({x}), then you must use the same variables names as returned by the Hook itself.
You have seen that when we use useState
Hook. we can write any name. eg.
const [data, setData] = useState()
or
const [isModalOpen, setIsModalOpen]
anything. 🤯
Use cases
If we are going to be using multiple instances of a Hook in a single component, then use array return.
If our component will only have 1 (or few instances) of a Hook, use object return.
Conclusion
Here we have discussed almost every aspect of custom React Hooks with examples. I hope this have helped you and now you will be able to create you own custom Hooks in React.
If you loved it. Please feel free to give your feedback or suggestions in comments. Also feel free to ask any query.