π Introduction
React has revolutionized frontend development with its component-based architecture. One of its most powerful features is custom hooks, which allow developers to encapsulate and reuse logic efficiently. If you find yourself using useState, useEffect, or useRef repeatedly across multiple components, creating a custom hook can significantly improve code organization and reusability.
π In this article, weβll cover:
- π― What custom hooks are and why theyβre useful
- π οΈ A step-by-step guide to creating a custom hook
- π Real-world examples of practical custom hooks
- β Best practices for writing custom hooks
π€ What is a Custom Hook in React?
A custom hook is a JavaScript function that starts with use
(e.g., useCustomHook
) and encapsulates reusable stateful logic using Reactβs built-in hooks. Instead of repeating logic in multiple components, we can extract it into a custom hook and use it across our application.
π‘ Example Use Case: Suppose you need to interact with localStorage
in multiple components. Instead of writing localStorage.getItem()
and localStorage.setItem()
repeatedly, you can create a custom hook to handle this logic.
ποΈ How to Create a Custom Hook
To create a custom hook, follow these steps:
- π·οΈ Define a function with a name starting with use.
- π Use built-in hooks like useState, useEffect, or useContext inside the function.
- π€ Return values or functions that can be used by components.
- π Import and use the custom hook in your components.
Letβs now build some practical custom hooks that solve real-world problems.
Example 1: ποΈ useLocalStorage (Managing Local Storage in React)
Local storage is commonly used to persist user preferences or session data. Letβs create a custom hook for handling local storage.
Step 1: π οΈ Create the Hook (useLocalStorage.js)
import { useState, useEffect } from "react";
function useLocalStorage(key: string, initialValue: any) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error("Error accessing localStorage", error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue));
} catch (error) {
console.error("Error saving to localStorage", error);
}
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
export default useLocalStorage;
Step 2: π― Use It in a Component
import React from "react";
import useLocalStorage from "./useLocalStorage";
function App() {
const [name, setName] = useLocalStorage("username", "Guest");
return (
<div>
<h2>Welcome, {name}! π</h2>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
}
export default App;
β Why This is Useful:
- πΎ Saves user input persistently in local storage.
- π Retains values even after page refresh.
Example 2: π useFetch (Handling API Requests in React)
Fetching data is a common operation in React applications. Instead of writing fetch() calls repeatedly, we can create a reusable hook for API requests.
Step 1: π οΈ Create the Hook (useFetch.js)
import { useState, useEffect } from "react";
function useFetch(url: string) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then((res) => {
if (!res.ok) throw new Error("Failed to fetch");
return res.json();
})
.then((data) => setData(data))
.catch((error) => setError(error))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
export default useFetch;
Step 2: π― Use It in a Component
import React from "react";
import useFetch from "./useFetch";
function UsersList() {
const { data, loading, error } = useFetch("https://jsonplaceholder.typicode.com/users");
if (loading) return <p>β³ Loading...</p>;
if (error) return <p>β Error: {error.message}</p>;
return (
<ul>
{data.map((user: any) => (
<li key={user.id}>π€ {user.name}</li>
))}
</ul>
);
}
export default UsersList;
β Why This is Useful:
- π Centralizes API fetching logic.
- β‘ Handles loading and error states efficiently.
π Best Practices for Writing Custom Hooks
- π― Keep Hooks Focused: Each hook should have a single responsibility.
- π·οΈ Follow the Naming Convention: Always start function names with use.
- π Use Dependency Arrays Wisely: Avoid unnecessary re-renders in useEffect.
- π Ensure Hooks Are Reusable: Avoid using component-specific logic inside hooks.
- π§ͺ Test Custom Hooks: Use Jest and React Testing Library to ensure they work correctly.
π Conclusion
Custom hooks in React allow developers to abstract logic, improve code reusability, and enhance maintainability. Whether it's handling local storage, API calls, or dark mode toggling, custom hooks provide an elegant solution to avoid redundant code.
By following best practices and structuring hooks properly, you can build efficient and reusable components that scale well in any React project.
Would you like to explore more complex custom hooks? Let me know in the comments! π
Top comments (0)