Module 2: Hooks & Lifecycle

useState, useEffect, and the Rules of Hooks.

1. Deep Dive: The Lifecycle of State

1.1 State is a Snapshot

This is the most common point of confusion. Setting state does not change the variable immediately. It schedules a re-render with the new value.

const [count, setCount] = useState(0);

function handleClick() {
  setCount(count + 1); // Schedules update to 1
  setCount(count + 1); // Schedules update to 1 (still uses old 'count' value of 0)
  setCount(count + 1); // Schedules update to 1
  console.log(count);  // Prints 0!
}

To fix this, use the updater function: setCount(prev => prev + 1).

1.2 The Dependency Array

useEffect is not a lifecycle method like componentDidMount. It is a synchronization mechanism. "Synchronize this code with these variables."

1.3 Cleanup Functions

If your effect subscribes to something (like a WebSocket or a Timer), you MUST return a cleanup function. React runs this function before the component unmounts or before the effect runs again.

useEffect(() => {
  const timer = setInterval(tick, 1000);
  return () => clearInterval(timer); // The Cleanup
}, []);

1.4 Industry Standard: Custom Hooks

Senior developers rarely write complex useEffect logic directly inside UI components. They extract it into Custom Hooks. This separates logic from view.

// Instead of writing useEffect in every component:
function UserProfile({ id }) {
  const { data, loading, error } = useFetch(`/users/${id}`);
  // ... render logic
}
📚 Official Documentation References
REQ-003 To Do

Build "SmartCounter" with History

User Story

As a user, I want to see a history of my counter values so I can track my changes over time.

Acceptance Criteria
  • Create a SmartCounter component.
  • State: Track count (number) and history (array of numbers).
  • Actions: Add Increment (+1) and Decrement (-1) buttons.
  • Logic: Whenever count changes, append the new value to history.
  • Display: Show the current count and a list of previous values.
Senior Bonus
  • Auto-Increment: Add a "Start/Stop" button that increments the counter every second using setInterval.
  • Cleanup: Ensure the interval is cleared when the component unmounts or when "Stop" is clicked.
  • Bug Prevention: Ensure the interval doesn't speed up if "Start" is clicked multiple times.
REQ-004 Custom Hook: useWindowSize

User Story: As a developer, I want a reusable hook to track the window dimensions so I can make responsive logic in JS.

Acceptance Criteria:

  • Create `useWindowSize.ts`.
  • Return `{ width, height }`.
  • Add event listener for `resize`.
  • Crucial: Clean up the event listener on unmount to prevent memory leaks.

2. The Setup Challenge

🐛 Scenario: The Infinite Loop

The Setup:

function BadComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1);
  }); // Missing dependency array!

  return <div>{count}</div>;
}

The Bug: This component crashes the browser tab. Why?

The Task:

  1. Explain the cycle: Render -> Effect -> SetState -> Render...
  2. Fix it so it only runs once on mount.
  3. Challenge: How would you make it run only when a prop userId changes?