Neural Sync Active
React Hooks — useState, useEffect, useContext
Registry Synced
React Hooks — useState, useEffect, useContext
948 words
5 min read
Reading compass
Now · 🎯 Learning Objectives
React Hooks — useState, useEffect, useContext
🎯 Learning Objectives
- Use useState for local component state
- Handle side effects with useEffect
- Share data across components with useContext
- Create custom hooks for reusable logic
- Understand dependency arrays and cleanup
1. useState — Local State
jsxconst [count, setCount] = React.useState(0); const [user, setUser] = React.useState(null); const [items, setItems] = React.useState([]);
Updating state with previous value:
jsxfunction Counter() { const [count, setCount] = React.useState(0); // Functional update — safe when depending on previous state function increment() { setCount(prev => prev + 1); setCount(prev => prev + 1); // Adds 2 total } return <button onClick={increment}>{count}</button>; }
2. useEffect — Side Effects
useEffect runs after the component renders. Used for: data fetching, subscriptions, timers, DOM manipulation, logging.
jsxfunction UserProfile({ userId }) { const [user, setUser] = React.useState(null); const [loading, setLoading] = React.useState(true); React.useEffect(() => { // This runs after every render (by default) fetch(`/api/users/${userId}`) .then(res => res.json()) .then(data => { setUser(data); setLoading(false); }); }, [userId]); // Only re-runs when userId changes if (loading) return <p>Loading...</p>; return <h1>{user.name}</h1>; }
Dependency array patterns:
jsx// Runs after EVERY render (no array) useEffect(() => { /* ... */ }); // Runs ONCE (on mount) — empty array useEffect(() => { fetch('/api/initial').then(setData); }, []); // Runs when userId or role changes useEffect(() => { fetchUser(userId, role).then(setUser); }, [userId, role]); // Cleanup function — runs before unmount and before re-run useEffect(() => { const timer = setInterval(() => tick(), 1000); return () => clearInterval(timer); // Cleanup! }, []);
Common mistakes:
- Missing dependencies: Stale closures, outdated values
- Infinite loops: Updating state without proper dependency array
- No cleanup: Timers/subscriptions leak memory
3. useContext — Global State
Context provides a way to share data (like theme, auth status, locale) without prop drilling.
jsx// 1. Create context const ThemeContext = React.createContext('light'); // 2. Provider component function App() { const [theme, setTheme] = React.useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Toolbar /> <Content /> </ThemeContext.Provider> ); } // 3. Consumer with useContext function Toolbar() { const { theme, setTheme } = React.useContext(ThemeContext); return ( <div style={{ background: theme === 'dark' ? '#333' : '#fff' }}> <button onClick={() => setTheme( theme === 'dark' ? 'light' : 'dark' )}> Toggle Theme </button> </div> ); }
4. Custom Hooks
Extract reusable logic into custom hooks (functions starting with
use):jsx// Custom hook for form input handling function useInput(initialValue) { const [value, setValue] = React.useState(initialValue); function handleChange(e) { setValue(e.target.value); } function reset() { setValue(initialValue); } return { value, onChange: handleChange, reset, // Spread these props onto an <input> bind: { value, onChange: handleChange } }; } // Usage function LoginForm() { const username = useInput(''); const password = useInput(''); function handleSubmit(e) { e.preventDefault(); login(username.value, password.value); username.reset(); password.reset(); } return ( <form onSubmit={handleSubmit}> <input type="text" {...username.bind} placeholder="Username" /> <input type="password" {...password.bind} placeholder="Password" /> <button type="submit">Login</button> </form> ); }
5. Rules of Hooks
- Only call hooks at the top level — not in loops, conditions, or nested functions
- Only call hooks from React function components — not regular JavaScript functions
- Custom hooks must start with
use— so React can detect violations
jsx// ❌ BAD — conditional hook call if (isLoggedIn) { useEffect(() => { /* ... */ }, []); } // ✅ GOOD — hooks always at top level useEffect(() => { if (!isLoggedIn) return; // ... }, [isLoggedIn]);
6. Practice Questions
Q1: What problem does useEffect solve?Answer: It handles side effects (data fetching, subscriptions, timers, manual DOM changes) in functional components. It runs after rendering and can optionally clean up after the component unmounts. Q2: What does the dependency array control?Answer: It tells React when to re-run the effect. Empty array[]= run once on mount.[dep]= re-run whendepchanges. No array = run after every render. Q3: Why do we need cleanup in useEffect?Answer: To prevent memory leaks and bugs. Cleanup runs before unmount and before re-running the effect. Used for: clearing timers, unsubscribing from events, aborting fetch requests. Q4: What is useContext used for?Answer: Accessing context values without prop drilling. It takes a Context object and returns its current value (from the nearest Provider above in the component tree). Q5: What's the difference between useState and useRef?Answer:useStatetriggers re-render when changed.useRefpersists values across renders without causing re-renders. Use refs for DOM references, previous values, or instance variables. Q6: Can you call hooks conditionally?Answer: No. React relies on the order and number of hooks being consistent across renders. Conditional hook calls break this and cause bugs. Move conditions inside the hook instead. Q7: Write a custom hook that tracks window width.jsxfunction useWindowWidth() { const [width, setWidth] = React.useState(window.innerWidth); React.useEffect(() => { function handleResize() { setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return width; } // Usage: const width = useWindowWidth();Q8: What causes infinite loop in useEffect?Answer: Updating state inside useEffect without specifying dependencies (or with dependencies that change every render):useEffect(() => { setCount(count + 1) })— runs, sets state, re-renders, runs again...
📐 Key Concepts
| Hook | Purpose | Returns |
|---|---|---|
useState | Local state | [value, setter] |
useEffect | Side effects | void (or cleanup function) |
useContext | Context consumption | Context value |
| Custom hook | Reusable logic | Any value(s) |
🔗 Cross-References
- Next: React Router & State Management Join Discord Previous2.1 React IntroductionNext4.1 React Router & State Management