Capítulo 36 de 116

Chapter 36: Synchronizing with Effects

Core Idea

An Effect lets a component synchronize itself with something outside React (a browser API, a server connection, a third-party widget) after rendering commits — unlike an event handler, an Effect isn't tied to one specific user interaction; it's tied to the component being displayed with a particular set of reactive values at all.

Key Concepts

  • Effects vs. events: an event handler runs because the user did something specific (a click, a submit); an Effect runs because the component rendered (initially, and again whenever a dependency changes) — it's about staying synchronized with the outside world, not about responding to one interaction.
  • Basic shape: useEffect(() => { /* synchronization code */ return () => { /* cleanup */ }; }, [dependencies]) — the function body runs after commit; the optional returned function cleans up before the next run (or on unmount).
  • The dependency array controls when it re-runs: omit it entirely and the Effect runs after every render; pass [] and it runs only once after the initial mount; pass [a, b] and it re-runs whenever a or b changes between renders.
  • Cleanup exists to undo the previous synchronization before starting a new one — e.g. a chat-room-connecting Effect's cleanup disconnects the old room before the next run connects to a new one; without cleanup, switching rooms would leave old connections open.
  • Effects fire twice in development (Strict Mode) on purpose: mount → cleanup → mount again, specifically to surface Effects whose cleanup is missing or incorrect — an Effect that's properly written to synchronize/desynchronize should be unaffected by this double-run other than briefly.
  • Writing a good Effect, in order: (1) write the synchronization logic assuming a single run, (2) specify the dependency array truthfully (every reactive value the Effect's body reads must be listed — see Ch 40), (3) add cleanup if the synchronization needs to be undone.

Code Examples

useEffect(() => {
  const connection = createConnection(roomId);
  connection.connect();
  return () => connection.disconnect(); // cleanup: undo before next run/unmount
}, [roomId]);
  • What it demonstrates: the connect/disconnect symmetry an Effect's body and cleanup should have — every "start doing X" needs a matching "stop doing X" so switching roomId doesn't leak the old connection.

Key Takeaways

  1. Ask "does this run because of a specific interaction, or because the component needs to stay synced with something external?" — the former is an event handler, the latter is an Effect.
  2. Every dependency in the array should be there because the Effect's body actually reads it — not chosen to control timing (Ch 40 covers this in depth).
  3. The Strict Mode double-invoke in development is a bug detector, not a bug — if it breaks your Effect, the Effect's cleanup is incomplete.

Connects To

  • Ch 37 (You Might Not Need an Effect): cases where reaching for useEffect at all is the wrong call.
  • Ch 38 (Lifecycle of Reactive Effects): the deeper mental model for how dependencies drive re-synchronization.
  • Ch 53 (useEffect): the full API reference.