useEffect lets a component subscribe to something React does not own. The render stays pure, and both the setup and the cleanup belong in the effect. The old habit of setting state during render waits until after the browser paints.
A cleanup is required when the effect opens a socket, a timer, or a subscription. The dependency array is the second argument. For a room that connects once, that array is empty: []. The reference is useEffect, and the same page also appears as a bare address:
https://react.dev/reference/react/useEffect
What the effect is for
An effect synchronizes the component with an external system.
- open a connection
- keep it in step with props
- compare the room id
- reconnect when the id changes
- compare the room id
- close it from the returned function
React's order of work is fixed:
- React renders the component.
- The browser paints.
- The effect runs.
- return a cleanup
- leave the socket open after unmount
Effects are an escape hatch. If the value can be calculated during render, it does not belong in an effect.
Timing
| Hook | Runs | Use it for |
|---|---|---|
useEffect | after paint | subscriptions, fetching, logging |
useLayoutEffect | before paint | measuring the DOM |
A connection
import { useEffect } from "react";
export function Room({ roomId }) {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]);
}
The same steps, without a language tag:
connect
disconnect




The dependency list
If the effect reads roomId, then roomId belongs in the array. React runs the cleanup before the next effect, and once more on unmount.1
Markup inside the paragraph
Water is H2O, and an effect that runs on every change grows like x2.
MDX that this page does not compile
This file is .mdx. The CMS stores the body as Markdown, so an import, an expression, and a component stay as text:
import { useEffect } from "react";
The doubled count is {count * 2}.
{/* cleanup belongs next to setup */}
<Callout>Do not start a subscription during render.</Callout>
Footnotes
-
The cleanup from the previous render runs before the next effect, then again when the component unmounts. ↩