Quiz 2

React Hooks — useState, useEffect, useContext

948 words
5 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# 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 **Upd...

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

jsx
const [count, setCount] = React.useState(0);
const [user, setUser] = React.useState(null);
const [items, setItems] = React.useState([]);
Updating state with previous value:
jsx
function 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.
jsx
function 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

  1. Only call hooks at the top level — not in loops, conditions, or nested functions
  2. Only call hooks from React function components — not regular JavaScript functions
  3. 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 when dep changes. 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: useState triggers re-render when changed. useRef persists 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.
jsx
function 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

HookPurposeReturns
useStateLocal state[value, setter]
useEffectSide effectsvoid (or cleanup function)
useContextContext consumptionContext value
Custom hookReusable logicAny value(s)

🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.