Component Lifecycle
A component progresses through mounting, updating, and unmounting phases that determine when rendering and cleanup occur. In modern React, function components express lifecycle behavior primarily with rendering plus Hooks such as `useEffect`, rather than class lifecycle methods.
Mounting is the initial appearance of a component. React renders the component, commits the resulting DOM changes, and then runs relevant passive Effects such as those declared with `useEffect` after the commit.
Updating happens when a component renders again because its state, props, or relevant context changes. React calculates the next UI, commits necessary changes, and reruns Effects when their dependency values have changed; an Effect's previous cleanup runs before its next setup when that Effect is being re-synchronized.
Unmounting occurs when React removes a component from the rendered tree. Cleanup functions returned from Effects are used to release subscriptions, timers, event listeners, connections, and other resources associated with the component.
The lifecycle should be thought of as synchronization with external systems rather than a fixed sequence of class methods. Effects are not needed for ordinary calculations derived from props or state, and unnecessary Effects can create extra renders or synchronization bugs.
Key points
- Mounting creates the component instance in the rendered tree and establishes its Effects.
- Rendering should remain free of side effects; it describes the UI for the current inputs.
- Effects synchronize a component with external systems after rendering and can return cleanup logic.
- Dependency changes can cause an Effect to clean up and run again.
- Unmount cleanup prevents subscriptions, timers, listeners, and similar resources from leaking.
Interview questions
How would you design cleanup for a WebSocket subscription?
The Effect would establish the connection or subscription and return a cleanup function that closes or unsubscribes it. Dependencies should identify the values that determine which connection is needed, such as a server URL or room identifier. When those values change, the old connection is cleaned up before the new one is established. Unmounting also invokes the cleanup, preventing the obsolete connection from remaining active.
What are the main phases of a React component lifecycle?
Conceptually, a component goes through mounting, updating, and unmounting. Mounting is when it first enters the rendered tree, updating occurs when its inputs cause another render, and unmounting removes it. In function components, Hooks provide the mechanisms for reacting to these transitions. `useEffect` is commonly used for synchronization and cleanup.
When does `useEffect` run, and what does its cleanup function do?
An Effect runs after React commits the component's rendered output. With dependencies, React reruns the Effect when a dependency changes according to dependency comparison. Before replacing an existing Effect setup, React runs its cleanup, and it also runs the cleanup when the component unmounts. Cleanup should release resources established by that Effect.
Why should side effects not be performed during rendering?
Rendering is expected to be a pure calculation from inputs to UI. React may render more than once, abandon a render, or otherwise invoke rendering in ways that make side effects there unreliable. External mutations belong in event handlers or appropriate Effects. Keeping rendering pure makes React's scheduling and reconciliation model safer to reason about.
How do you prevent an Effect from using stale values?
The Effect should declare the reactive values it reads as dependencies when those values determine the synchronization. React then reruns the Effect when those dependencies change. Omitting a relevant dependency can leave the Effect using a value captured by an earlier render. In some designs, restructuring the code can remove the need for an Effect altogether.