Loading...
Loading...
Hooks allow functional components to use state and lifecycle features without writing a class.
Rules of Hooks:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // Initial value = 0
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(prev => prev - 1)}>-1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Functional update (safer for async):
// ❌ Might read stale state
setCount(count + 1);
setCount(count + 1); // Still count+1, not count+2!
// ✅ Always uses latest state
setCount(prev => prev + 1);
setCount(prev => prev + 1); // count+2 ✅
Object state:
const [user, setUser] = useState({ name: '', email: '', age: 0 });
// ❌ Wrong — directly mutating
user.name = 'Rahul';
// ✅ Spread to create new object
setUser(prev => ({ ...prev, name: 'Rahul' }));
Array state:
const [items, setItems] = useState([]);
// Add item
setItems(prev => [...prev, newItem]);
// Remove item
setItems(prev => prev.filter(item => item.id !== id));
// Update item
setItems(prev => prev.map(item =>
item.id === id ? { ...item, completed: true } : item
));
Side effects: API calls, subscriptions, timers, document title updates.
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Effect runs after render
setLoading(true);
fetch(`https://api.example.com/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
// Cleanup function (runs before next effect or unmount)
return () => {
console.log('Cleanup: cancel pending requests');
};
}, [userId]); // Re-run when userId changes
if (loading) return <p>Loading...</p>;
return <div>{user?.name}</div>;
}
Effect patterns:
// Run once on mount (componentDidMount)
useEffect(() => {
document.title = 'WohoTech';
// Optional cleanup on unmount
return () => { document.title = 'App'; };
}, []);
// Run on every render (NO dependency array)
useEffect(() => {
console.log('Every render');
});
// Run when specific value changes
useEffect(() => {
localStorage.setItem('theme', theme);
}, [theme]);
// Cleanup example — event listener
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
// Timer with cleanup
useEffect(() => {
const timer = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(timer);
}, []);
Share data across component tree without passing props at every level.
import { createContext, useContext, useState } from 'react';
// 1. Create context
const ThemeContext = createContext('light');
const UserContext = createContext(null);
// 2. Provide context (wrap parent)
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState({ name: 'Rahul', role: 'student' });
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<UserContext.Provider value={user}>
<Header />
<Main />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
// 3. Consume anywhere deep in tree
function Header() {
const { theme, setTheme } = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<header className={theme === 'dark' ? 'bg-black text-white' : 'bg-white'}>
<span>Welcome, {user.name}</span>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
Toggle {theme === 'light' ? '🌙' : '☀️'}
</button>
</header>
);
}
import { useRef, useEffect } from 'react';
function SearchInput() {
const inputRef = useRef(null); // DOM reference
const renderCount = useRef(0); // Persist value across renders (no re-render)
useEffect(() => {
inputRef.current.focus(); // Auto-focus on mount
renderCount.current += 1; // Won't trigger re-render
console.log(`Rendered ${renderCount.current} times`);
});
return <input ref={inputRef} placeholder="Search..." />;
}
// useRef vs useState
// useRef.current change → NO re-render
// useState change → triggers re-render
import { useMemo, useState } from 'react';
function ProductList({ products, searchTerm }) {
// ❌ Without useMemo — filters on EVERY render
// const filtered = products.filter(p => p.name.includes(searchTerm));
// ✅ With useMemo — only re-filters when products or searchTerm changes
const filteredProducts = useMemo(() => {
console.log('Filtering...');
return products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [products, searchTerm]);
return (
<ul>
{filteredProducts.map(p => <li key={p.id}>{p.name} — ₹{p.price}</li>)}
</ul>
);
}
Prevents child component from re-rendering due to new function reference.
import { useCallback, useState, memo } from 'react';
// memo wraps child — only re-renders if props change
const Button = memo(({ onClick, label }) => {
console.log(`Button "${label}" rendered`);
return <button onClick={onClick}>{label}</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ❌ Without useCallback — new function on every Parent render
// const handleClick = () => setCount(c => c + 1);
// ✅ With useCallback — same function reference (Button won't re-render)
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // No dependencies, never changes
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<p>Count: {count}</p>
<Button onClick={handleClick} label="Increment" />
</div>
);
}
Alternative to useState for complex state with multiple sub-values.
import { useReducer } from 'react';
// Define state shape and actions
const initialState = { count: 0, step: 1, history: [] };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + state.step, history: [...state.history, state.count + state.step] };
case 'DECREMENT':
return { ...state, count: state.count - state.step };
case 'SET_STEP':
return { ...state, step: action.payload };
case 'RESET':
return initialState;
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function AdvancedCounter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count} | Step: {state.step}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+{state.step}</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-{state.step}</button>
<button onClick={() => dispatch({ type: 'SET_STEP', payload: 5 })}>Step=5</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
<p>History: {state.history.join(' → ')}</p>
</div>
);
}
// useFetch — reusable API fetching
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
setError(null);
fetch(url)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(data => { if (!cancelled) { setData(data); setLoading(false); } })
.catch(err => { if (!cancelled) { setError(err.message); setLoading(false); } });
return () => { cancelled = true; };
}, [url]);
return { data, loading, error };
}
// useLocalStorage — sync state with localStorage
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setStoredValue = useCallback((val) => {
setValue(val);
localStorage.setItem(key, JSON.stringify(val));
}, [key]);
return [value, setStoredValue];
}
// useDebounce — delay state update
function useDebounce(value, delay = 500) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// Usage
function SearchComponent() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 400);
const { data, loading } = useFetch(`/api/search?q=${debouncedQuery}`);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
{loading ? <p>Searching...</p> : <Results data={data} />}
</div>
);
}
| Hook | Purpose | When to use | |---|---|---| | useState | Local component state | Simple values, forms | | useEffect | Side effects | API calls, subscriptions, timers | | useContext | Global state access | Theme, auth, language | | useRef | DOM access + persist | Focus, animate, store prev value | | useMemo | Memoize calculation | Expensive computations | | useCallback | Memoize function | Pass stable callbacks to children | | useReducer | Complex state logic | Multiple related state values | | Custom Hook | Reusable stateful logic | Share logic between components |
React 16.8 (2019) mein aaye. Class components ki complexity hatane ke liye — lifecycle methods confusing the, aur logic reuse hard tha. Hooks se functional components mein state aur side effects possible hue.
Without dependency array → every render pe run hoga. Empty [] → only on mount. [var] → var change hone pe run hoga. Wrong dependencies → stale closure bugs ya infinite loops.
useMemo ek value memoize karta hai (expensive calculation). useCallback ek function memoize karta hai (prevent re-creation on every render). Dono unnecessary re-renders rokne ke liye use hote hain.
React shallow comparison se re-render detect karta hai. Direct mutation karne se React ko change pata nahi chalta. setState/useState se new reference milti hai → React re-renders.
JavaScript Complete Guide — Beginner to Advanced (2026)
40 min · Beginner
ToolsGit & GitHub Complete Guide — Version Control for Students (2026)
20 min · Beginner
DatabaseSQL Complete Guide — Database Queries for B.Tech & Placements (2026)
30 min · Intermediate
PythonPython for Beginners: 15-Day Complete Learning Plan
15 min · Beginner
DSAData Structures and Algorithms: Full Fundamentals + Interview Prep
35 min · Intermediate
JavaTop 50 Java Interview Questions — B.Tech Placements 2026
18 min · Advanced
Your feedback helps us improve notes and tutorials.