Modern React 10: Effects, synchronization, and cleanup
This is part 10 of the Modern React development series.
Effects connect React to systems outside the render tree. They are not a general place for all after-render logic. Their job is synchronization: start something external with the current props and state, then stop or update it when those values change.
Concept
useEffect runs after React commits UI to the screen. An Effect can subscribe to a browser API, connect to a service, start a timer, or coordinate with a non-React widget. Its cleanup function stops the previous synchronization.
Terms
- Effect: A Hook callback used to synchronize with an external system after render commit.
- Cleanup: A function returned from an Effect to stop the previous synchronization.
- Dependency array: The list of reactive values that tell React when the Effect needs to resynchronize.
- Reactive value: A prop, state value, or variable declared inside the component that can differ between renders.
Mental model
Think of each Effect as a plug. The Effect body plugs the component into an external system. The cleanup unplugs it before React plugs in the next version.
How it is used
Use Effects for subscriptions, browser events, timers, media APIs, analytics page visibility events, and external widgets. Use render calculations for derived values and event handlers for user-triggered work.
How to use it
- Name the external system the Effect synchronizes with.
- Read every reactive value the synchronization needs.
- Include those reactive values in the dependency array.
- Return cleanup when the external system needs to unsubscribe, disconnect, cancel, or clear.
- Split unrelated synchronization processes into separate Effects.
Example: Browser online status
import { useEffect, useState } from "react";
export function OnlineStatus() { const [online, setOnline] = useState(navigator.onLine);
useEffect(() => { const update = () => setOnline(navigator.onLine);
window.addEventListener("online", update); window.addEventListener("offline", update);
return () => { window.removeEventListener("online", update); window.removeEventListener("offline", update); }; }, []);
return <p>{online ? "Online" : "Offline"}</p>;}Online
The component subscribes to browser events after commit and removes the listeners on cleanup.
Example: Chat room subscription
import { useEffect } from "react";import { createChatConnection } from "./chat";
export function ChatRoom({ roomId }: { roomId: string }) { useEffect(() => { const connection = createChatConnection(roomId); connection.connect();
return () => connection.disconnect(); }, [roomId]);
return <h1>Room {roomId}</h1>;}Room general
When roomId changes, React cleans up the old connection and starts a new one for the next room.
Details to watch
- External system: If there is no external system, the code often belongs in render or an event handler.
- Dependencies: The dependency list describes values used by the synchronization, not values you would prefer to ignore.
- Cleanup: Subscriptions, intervals, sockets, observers, and in-flight manual work usually need cleanup.
- Strict Mode: Development Strict Mode may run setup and cleanup more than once to surface unsafe Effects.
Series navigation
- Previous: Part 9: Refs and DOM escape hatches
- Next: Part 11: Custom hooks as reuse boundaries
- Series index: Modern React development