Custom Hooks
Custom hooks let you extract component logic into reusable functions.
Naming Convention
Custom hooks must start with use:
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount((c) => c + 1);
const decrement = () => setCount((c) => c - 1);
return { count, increment, decrement };
}
Using a Custom Hook
function Counter() {
const { count, increment, decrement } = useCounter(0);
return (
<div>
<p>{count}</p>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
Each component gets its own state.
Example: useLocalStorage
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
Example: useFetch
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((json) => {
setData(json);
setLoading(false);
});
}, [url]);
return { data, loading };
}
Rules of Hooks
- Only call hooks at the top level
- Only call hooks from React functions or custom hooks
- Prefix custom hooks with
use
Best Practices
- One hook per piece of logic
- Keep hooks small and focused
- Return arrays for multiple values, objects for named values
- Test custom hooks with
renderHook
Common Mistakes
- Calling hooks inside conditions or loops
- Not including all dependencies in effects
- Naming without
useprefix breaks linting rules