The two most fundamental React hooks — master these and you can build almost anything.
useState lets a component "remember" values between renders.
1import { useState } from "react";
2
3function Counter() {
4 const [count, setCount] = useState(0);
5
6 return (
7 <div>
8 <p>Count: {count}</p>
9 <button onClick={() => setCount(count + 1)}>+</button>
10 <button onClick={() => setCount(count - 1)}>-</button>
11 </div>
12 );
13}1// ✅ Safe — uses functional update
2setCount(prev => prev + 1);
3
4// ❌ Risky — may use stale state
5setCount(count + 1);useEffect runs side effects after render — data fetching, subscriptions, DOM manipulation.
1import { useState, useEffect } from "react";
2
3function UserProfile({ userId }) {
4 const [user, setUser] = useState(null);
5 const [loading, setLoading] = useState(true);
6
7 useEffect(() => {
8 setLoading(true);
9 fetch(`/api/users/${userId}`)
10 .then(res => res.json())
11 .then(data => {
12 setUser(data);
13 setLoading(false);
14 });
15 }, [userId]); // Re-runs when userId changes
16
17 if (loading) return <p>Loading…</p>;
18 return <h2>{user?.name}</h2>;
19}| Usage | Behaviour |
|---|---|
useEffect(fn) | Runs after every render |
useEffect(fn, []) | Runs once (on mount) |
useEffect(fn, [a, b]) | Runs when a or b changes |
Always return a cleanup function when creating subscriptions or timers:
1useEffect(() => {
2 const id = setInterval(() => setTick(t => t + 1), 1000);
3 return () => clearInterval(id); // Runs on unmount
4}, []);Common mistake: Missing cleanup causes memory leaks in long-running apps.