React Hooks Explained: A Visual Guide for 2026
Source: Dev.to
useState
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
setCount(count + 1)}>
Clicked {count} times
);
}When to use: Anything the component needs to remember between renders — form values, toggles, counters.
Gotcha: State updates are asynchronous.
// ❌ This won't work as expected
setCount(count + 1);
setCount(count + 1); // Both use the same `count` value
// ✅ Use the updater function instead
setCount(prev => prev + 1);
setCount(prev => prev + 1); // Now it's +2useEffect
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Runs after every render where userId changes
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(setUser);
}, [userId]); // dependency array
return {user?.name};
}Dependency array
| Dependency | Behavior |
|---|---|
[] | Run once on mount |
[value] | Run when value changes |
| none | Run after every render (usually wrong) |
Cleanup
useEffect(() => {
const subscription = subscribe(userId);
return () => {
subscription.unsubscribe(); // cleanup!
};
}, [userId]);useContext
import { createContext, useContext, useState } from 'react';
// 1. Create the context
const ThemeContext = createContext('light');
// 2. Provide it at the top
function App() {
const [theme, setTheme] = useState('dark');
return (
);
}
// 3. Use it anywhere in the tree
function Button() {
const { theme } = useContext(ThemeContext);
return Click me;
}useRef
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
Focus
);
}Also useful for storing values that shouldn’t trigger re‑renders:
const renderCount = useRef(0);
renderCount.current += 1; // Doesn't cause re-renderuseMemo
function ProductList({ products, minPrice }) {
// Recalculates only when `products` or `minPrice` change
const filtered = useMemo(() => {
return products.filter(p => p.price >= minPrice);
}, [products, minPrice]);
return (
{filtered.map(p => - {p.name}
)}
);
}When to use: Only for genuinely expensive calculations that you’ve measured cause performance issues.
useCallback
import { useCallback } from 'react';
function Parent({ id }) {
// Without useCallback, a new function is created every render
const handleClick = useCallback(() => {
doSomethingWith(id);
}, [id]);
return ;
}When to use: When passing callbacks to React.memo‑wrapped children to avoid unnecessary re‑renders.
useReducer
import { useReducer } from 'react';
const initialState = { count: 0, error: null, loading: false };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'reset':
return initialState;
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
Count: {state.count}
dispatch({ type: 'increment' })}>+
dispatch({ type: 'decrement' })}>-
);
}Use instead of useState when: multiple pieces of state are related and change together.
Custom Hooks (Composability)
// Custom hook for API calls
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(r => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
})
.then(data => { setData(data); setLoading(false); })
.catch(err => { setError(err.message); setLoading(false); });
}, [url]);
return { data, loading, error };
}
// Using the custom hook
function UserList() {
const { data, loading, error } = useApi('/api/users');
if (loading) return ;
if (error) return ;
return
{data.map(u => - {u.name}
)}
;
}Hook Overview
| Hook | Purpose |
|---|---|
useState | Local state |
useEffect | Side effects, data fetching |
useContext | Share state without props |
useRef | DOM refs, mutable values |
useMemo | Memoize expensive calculations |
useCallback | Stable callback references |
useReducer | Complex state logic |