⚛️
Web Dev
React Hooks: Complete Notes
useState, useEffect, useContext, useRef, useMemo — every hook explained with real examples.
Back to Notes Library
React Hooks: Complete Notes
useState
tsx
import { useState } from 'react';
// Basic state
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [user, setUser] = useState<User | null>(null);
// Functional update (when new state depends on old)
setCount(prev => prev + 1);
// Object state — always spread, never mutate!
const [form, setForm] = useState({ name: '', email: '' });
setForm(prev => ({ ...prev, name: 'Alice' }));
// Lazy initialization (expensive initial state)
const [data, setData] = useState(() => computeExpensiveValue());useEffect
tsx
import { useEffect } from 'react';
// Run after every render
useEffect(() => {
console.log("rendered");
});
// Run once (on mount)
useEffect(() => {
fetchData();
}, []);
// Run when dependencies change
useEffect(() => {
fetchUser(userId);
}, [userId]);
// Cleanup (unsubscribe, clear timers)
useEffect(() => {
const id = setInterval(() => tick(), 1000);
return () => clearInterval(id); // cleanup function
}, []);
// Async in useEffect
useEffect(() => {
let cancelled = false;
async function load() {
const data = await fetchData();
if (!cancelled) setData(data);
}
load();
return () => { cancelled = true; };
}, [id]);useRef
tsx
import { useRef } from 'react';
// DOM reference
const inputRef = useRef<HTMLInputElement>(null);
// In JSX: <input ref={inputRef} />
inputRef.current?.focus();
// Mutable value (no re-render on change)
const timerRef = useRef<NodeJS.Timeout | null>(null);
timerRef.current = setTimeout(() => {}, 1000);
// Previous value pattern
function usePrevious<T>(value: T) {
const ref = useRef<T>();
useEffect(() => { ref.current = value; });
return ref.current;
}useContext
tsx
import { createContext, useContext, useState } from 'react';
// 1. Create context
interface ThemeContextType {
theme: 'light' | 'dark';
toggle: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
// 2. Provide context (wrap your app or section)
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
// 3. Use context in any child component
function ThemedButton() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error("Must be inside ThemeProvider");
return <button onClick={ctx.toggle}>Mode: {ctx.theme}</button>;
}useMemo & useCallback
tsx
import { useMemo, useCallback } from 'react';
// useMemo — memoize expensive computation
const sortedList = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items] // recalculate only when items changes
);
// useCallback — memoize function reference
const handleClick = useCallback(
(id: number) => {
deleteItem(id);
refetch();
},
[refetch] // new function only when refetch changes
);When to use:
useMemo— Expensive calculations, avoid unnecessary re-renders of pure componentsuseCallback— Passing callbacks to optimized child components wrapped in React.memo
useReducer
tsx
import { useReducer } from 'react';
type State = { count: number; error: string | null };
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset' }
| { type: 'setError'; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment': return { ...state, count: state.count + 1 };
case 'decrement': return { ...state, count: state.count - 1 };
case 'reset': return { count: 0, error: null };
case 'setError': return { ...state, error: action.payload };
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, error: null });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}Custom Hooks
tsx
// useFetch — data fetching hook
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch(url)
.then(r => r.json())
.then(d => { if (!cancelled) setData(d); })
.catch(e => { if (!cancelled) setError(e.message); })
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; };
}, [url]);
return { data, loading, error };
}
// useLocalStorage — sync state with localStorage
function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initial;
} catch { return initial; }
});
const setStored = (newValue: T) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
};
return [value, setStored] as const;
}Rules of Hooks
- Only call hooks at the top level (not inside loops, conditions, or nested functions)
- Only call hooks from React function components or custom hooks
- Custom hooks must start with "use"
- ESLint plugin
eslint-plugin-react-hooksenforces these rules automatically
Hook Quick Reference
| Hook | Purpose |
|---|---|
| useState | Local component state |
| useEffect | Side effects, subscriptions |
| useRef | DOM refs, mutable values |
| useContext | Global state consumption |
| useMemo | Memoize computed values |
| useCallback | Memoize function references |
| useReducer | Complex state with actions |
| useId | Unique IDs for accessibility |
| useTransition | Mark updates as non-urgent |
| useDeferredValue | Defer expensive re-renders |
10K+ Members Growing Daily
Get Free AI Notes Daily
Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!
📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel
No spam. Leave anytime.