In React development, custom hooks are a powerful way to reuse logic across components. Creating custom hooks helps you write cleaner, more maintainable code by encapsulating common functionality.
In this post, you’ll learn how to create and use custom hooks in React.
Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!
Now, let’s jump right into it!
What Are Custom Hooks in React?
A custom hook is a function that provides reusable logic by using React’s built-in hooks (useState
, useEffect
, etc.).
Its name starts with “use” (for example: useAuth
, useFetch
, etc.), so that React knows it’s a hook.
Custom hooks increase reusability and maintainability.
Steps for Creating Custom Hooks in React
Here’s the step-by-step guide for creating a custom counter hook:
Step 1: Create a Hook Function
function useCounter() {
}
export default useCounter;
Here,
- The function name starts with “use”, which is a convention for hooks.
- This ensures React treats it as a hook and applies hook rules properly.
Step 2: Add State and Functions
import { useState } from "react";
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialValue);
}
export default useCounter;
Here,
-
useState
is used to manage the count state. - increment, decrement, and reset functions modify the count state.
- This logic is reusable across multiple components.
Step 3: Return the values or functions that you need
import { useState } from "react";
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
export default useCounter;
Here,
- The custom hook now returns an object containing count and update functions.
- This allows any component to use these values and functions by destructuring.
Step 4: Use the custom hook in the counter component
import React from "react";
import useCounter from "./useCounter";
function CounterComponent() {
const { count, increment, decrement, reset } = useCounter(5);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
}
export default CounterComponent;
Here,
- The
useCounter
hook is imported and used inCounterComponent
. - The initial value 5 is passed as an argument.
- The count state and functions are extracted using object destructuring.
- Buttons are added to interact with the state using the hook’s functions.
By using this custom hook, you can reuse the same counter-logic in multiple components.
Why Use Custom Hooks in React?
Custom hooks are useful:
- Reusability increases.
- Code readability and maintainability improve.
- Reduces the need to write the same logic again and again.
Best Practices for Creating Custom Hooks
- Always start the name of the custom hook with “use” (
useCounter
,useFetch
, etc.). - To manage state and side effects, use built-in hooks (
useState
,useEffect
, etc.). - Try to create hooks that are reusable and generic.
- If a custom hook is only needed in a single component, then writing that in a separate file is not required.
Real-World Examples of Custom Hooks
Here are some of the custom hooks that are useful in real-world applications.
useLocalStorage – For Storing Data
If you want to store data in local storage, then it’s a good practice to create a custom hook instead of writing localStorage.setItem
and localStorage.getItem
in each component.
import { useState, useEffect } from "react";
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const savedValue = localStorage.getItem(key);
return savedValue ? JSON.parse(savedValue) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
export default useLocalStorage;
Explanation:
- Retrieves and stores values in localStorage automatically.
- Uses
useState
to manage stored data. - Uses
useEffect
to update localStorage when the value changes.
Now, you can use this in any component.
import React from "react";
import useLocalStorage from "./useLocalStorage";
function ThemeComponent() {
const [theme, setTheme] = useLocalStorage("theme", "light");
return (
<div>
<h2>Current Theme: {theme}</h2>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
</div>
);
}
export default ThemeComponent;
Explanation:
- Uses
useLocalStorage
to store and retrieve the theme. - Clicking the button toggles between “light” and “dark” mode.
useFetch – To Make API Calls Easy
You can create a custom hook for handling API calls instead of writing fetch()
in each component.
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) throw new Error("Failed to fetch data");
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Explanation:
- Manages API calls with fetch inside
useEffect
. - Handles loading, data, and error states.
- Fetches data when the URL changes.
Now, you can use this in any component.
import React from "react";
import useFetch from "./useFetch";
function UsersComponent() {
const { data, loading, error } = useFetch("https://jsonplaceholder.typicode.com/users");
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<h2>Users List</h2>
<ul>
{data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default UsersComponent;
Explanation:
- Fetches and displays a list of users.
- Handles loading and error states for a smooth UI.
- Uses useFetch to keep API logic reusable.
🎯Wrapping Up
That’s all for today!
For paid collaboration connect with me at : [email protected]
I hope this post helps you.
If you found this post helpful, here’s how you can support my work:
☕ Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.
Keep coding & happy learning!
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.