The cleanup is part of the setup.
A source-first reading of React effects, external systems and dependencies.

Effects synchronize with something outside React
An Effect is not a general-purpose box for any work you want to do after a component renders. The React reference frames useEffect as a way to synchronize a component with an external system: a connection, a browser API, a timer or a third-party widget whose lifetime React does not manage.
That framing makes cleanup easier to reason about. If setup opens a connection, cleanup should close it. If setup subscribes to an event, cleanup should unsubscribe. The two functions describe one synchronization contract rather than unrelated callbacks.
What React actually runs
React runs the setup when a component mounts. When dependencies change, React runs the previous cleanup before the next setup. It also runs cleanup when the component unmounts. In development, Strict Mode may run an additional setup-and-cleanup cycle to expose code that does not clean up correctly.
That behavior can look surprising if an Effect is treated as 'run once' initialization. A dependency array controls when re-synchronization is required; it is not a guarantee that an external connection will never be restarted. The correct mental model is to make setup and cleanup symmetrical.
A small connection example
The room identifier and server address are reactive inputs. When either changes, the old connection should be disconnected and a new one should take its place. The cleanup function closes over the connection made by its own setup, preventing it from accidentally disconnecting a newer instance.
useEffect(() => {
const connection = createConnection(serverUrl, roomId)
connection.connect()
return () => {
connection.disconnect()
}
}, [serverUrl, roomId])Dependencies are observable behavior
Every reactive value used inside an Effect belongs in its dependency list. Trying to suppress a linter warning without restructuring the effect often hides a real synchronization problem. A changed room identifier or server URL ought to produce a changed subscription; omitting it can leave the component talking to an obsolete external resource.
Not every value read by a component must cause reconnection, however. React documents Effect Events as a way to read the latest non-reactive values inside certain Effect-related callbacks without making the connection itself depend on those values. That is a more precise solution than inventing a dependency list that happens to stop a warning.
- Start by naming the external system and its lifecycle.
- Keep setup and cleanup together so their symmetry is visible.
- Include reactive dependencies used in the synchronization.
- Test switching inputs and unmounting, not just initial loading.
- Use the official reference before applying workarounds suggested for a different React release.
Follow the reference, then test
An excerpt from a documentation tool is useful when it gets you to the right page quickly. The primary React page remains the authority for precise behavior and caveats. Read the examples next to the dependency and troubleshooting guidance, then verify the effect against your real network or browser integration.
The strongest documentation workflow ends with an observable result: no leaked subscriptions, no stale connection and no unexpected update to an unmounted component. Source links make the reasoning auditable; runtime checks make it trustworthy.
Follow the sources.
References are presented for inspection and may point to a specific repository revision or official documentation edition.