React: From Components to Production · State, Events and Effects

useState and Event Handling

Store component state, update it correctly and handle events.

useState returns the current value and a setter. Setting state schedules a re-render; the new value is visible on the next render, not immediately. Use the updater form (setCount(c => c + 1)) when the next state depends on the previous one, and never mutate state objects — create new ones. Event handlers receive synthetic events with the same interface as native ones.

import { useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);
  const [profile, setProfile] = useState({ name: "", tags: [] as string[] });

  function addThree() {
    setCount((c) => c + 1);   // updater form: queued updates use the latest value
    setCount((c) => c + 1);
    setCount((c) => c + 1);
  }

  function addTag(tag: string) {
    setProfile((p) => ({ ...p, tags: [...p.tags, tag] }));   // new objects, no mutation
  }

  return (
    <div>
      <button onClick={addThree}>+3 ({count})</button>
      <input value={profile.name} onChange={(e) => setProfile((p) => ({ ...p, name: e.target.value }))} />
      <button onClick={() => addTag("react")}>Add tag</button>
    </div>
  );
}

Common pitfalls

  • Reading state right after setting it and expecting the new value.
  • Mutating arrays/objects (state.push(x)) — React won't re-render.
  • Calling the handler during render: onClick={handle()} instead of onClick={handle}.

Key points

  • State updates are asynchronous snapshots
  • Use updater functions for derived updates
  • Never mutate state

Further reading: react.dev: State

Practice questions

Why does calling setCount(count + 1) three times only add 1?

Answer

All three calls read the same count from the current render snapshot. Use setCount(c => c + 1) so each update builds on the previous queued value.

Why doesn't items.push(newItem); setItems(items) re-render?

Answer

The array reference didn't change, so React bails out. Create a new array: setItems([...items, newItem]).

Open in the app to mark lessons complete, take notes and track your progress.