Custom hooks let you extract and reuse stateful logic in React functional components. This guide covers what they are, why to use them, and how to create one.
A custom hook is a JavaScript function that:
use
(e.g., useCustomHook
).useState
, useEffect
) to manage state or side effects.It’s a way to share logic, not UI, across components.
graph TD
A[Component] --> B[Custom Hook]
B --> C[Reusable Logic]
C --> D[State]
C --> E[Side Effects]
B --> F[Returns Values]
F --> A
Identify repetitive logic (e.g., fetching data) and extract it.
use
Prefix the function with use
to follow React’s hook convention.
Leverage useState
, useEffect
, etc., within the custom hook.
Return what components need (e.g., data, setters).
useFetch
- Fetching DataHere’s a custom hook to fetch data:
// useFetch.js
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]); // Re-fetch if url changes
return { data, loading };
}
// Usage in a component
function App() {
const { data, loading } = useFetch('https://api.example.com/data');
return loading ? <p>Loading...</p> : <p>{data.message}</p>;
}
useFetch
signals it’s a hook.useState
for data/loading, useEffect
for fetching.{ data, loading }
for the component to use.useLocalStorage
- Syncing State with Local StorageWhen to Use: When you want to persist simple state (e.g., a user preference) in local storage.
// useLocalStorage.js
import { useState } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
return localStorage.getItem(key) || initialValue;
});
const setStoredValue = (newValue) => {
setValue(newValue);
localStorage.setItem(key, newValue);
};
return [value, setStoredValue];
}
// Usage
function ThemeSwitcher() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Switch to {theme === 'light' ? 'Dark' : 'Light'}
</button>
);
}
[value, setStoredValue]
—the stored value and an updater.useCounter
- Managing a CounterWhen to Use: When you need a reusable counter (e.g., for likes, scores) in multiple places.
// useCounter.js
import { useState } from 'react';
function useCounter(initialCount = 0) {
const [count, setCount] = useState(initialCount);
const increment = () => setCount(prev => prev + 1);
const decrement = () => setCount(prev => prev - 1);
const reset = () => setCount(initialCount);
return { count, increment, decrement, reset };
}
// Usage
function Counter() {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>Reset</button>
</div>
);
}
{ count, increment, decrement, reset }
—the count and control functions.