React Custom Hooks: The Best Way to Share Logic
By Mohiuddin Murad
October 15, 2023
React
Hooks
Custom Hooks
State Management

React Custom Hooks are reusable JavaScript functions whose names begin with "use" and that can call other Hooks (such as useState
, useEffect
). They provide a powerful way to extract and share stateful logic between components without altering the component hierarchy.
Why Use Custom Hooks?
- Reusability: They allow you to reuse logic across multiple components, adhering to the DRY (Don't Repeat Yourself) principle.
- Clean Code & Separation of Concerns: Custom Hooks help separate complex business logic from the UI rendering logic of a component, making the code cleaner, more readable, and easier to reason about.
- Maintainability: With the logic centralized in one place, it becomes much easier to update, debug, and maintain.
Example 1: useFetch
for Data Fetching
A common use case is creating a custom hook to handle data fetching, loading states, and errors.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err);
setData(null);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]); // Reruns the effect if the URL changes
return { data, loading, error };
}
// How to use it in a component
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`https://api.example.com/users/${userId}`);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
Example 2: useLocalStorage
for Persistent State
This hook simplifies the process of synchronizing component state with the browser's local storage.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// Get from local storage then
// parse stored json or return initialValue
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// How to use it in a component
function ThemeSwitcher() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<button onClick={toggleTheme}>
Current theme: {theme}
</button>
);
}
Custom Hooks are a fundamental pattern in modern React development, promoting cleaner, more modular, and highly maintainable codebases.