The React component lifecycle has three phases: mounting (the component appears on screen for the first time), updating (it renders again because its state, props or context changed) and unmounting (it is removed). In function components you react to these phases with useEffect: an effect with [] runs after mounting, an effect with dependencies runs after updates where they changed, and the function an effect returns runs on unmount.
The console under the preview shows each phase as it happens.
The console starts with render, count = 0 and mounted. Each click on Count logs a render and an update. Unmount logs unmounted; Mount again starts over from count = 0, because the state was thrown away with the component.
Mounting
A component mounts the first time React renders it in a given place in the tree. React calls the function, creates the DOM nodes from the JSX it returns, inserts them, lets the browser paint, and then runs the component's effects.
useEffect(fn, []) is where you start things that should exist for as long as the component is on screen: a subscription, a timer, a connection, a first data request. It runs after the paint, so it never delays the first frame.
Updating
A component updates (renders again) when:
- its own state changes,
- its parent renders again (by default, every child renders too, whether or not its props changed),
- a context it reads changes.
After each update, React runs the effects whose dependencies changed. Before running an effect again, it runs that effect's previous cleanup, so the old subscription is closed before the new one opens. The rendering page covers what triggers a render in more detail.
Unmounting
A component unmounts when its parent stops rendering it (as {show && <Counter />} does above), when its key changes, or when the root itself is unmounted. React removes its DOM nodes, runs every effect's cleanup, and discards its state. If the component appears again later, it is a brand new instance.
Parents and children
React renders top down but runs effects bottom up. When a parent and child mount together, the child's effects run first, so a parent's effect can rely on its children being fully set up.
The mount logs render Parent, render Child, Child effect, Parent effect. Press Remove and the cleanups run the other way round: Parent cleanup, then Child cleanup.
Render and commit phases
Each update goes through two phases:
- Render phase. React calls your components to compute the new JSX and compares it with the previous result. Nothing on screen changes yet. React may pause this work, run it twice in StrictMode, or throw it away, which is why rendering must be pure: no requests, no subscriptions, no changes to outside variables.
- Commit phase. React applies the differences to the DOM, attaches refs, and runs
useLayoutEffectcallbacks, all before the browser paints.useEffectcallbacks run after that, usually once the browser has painted. When a click or another discrete input caused the update, React runs them straight after the layout effects instead, so the result is ready before the next event.
Every click logs the three lines in the same order. These effects have no dependency array, so they run after every render. Add [] as a second argument to both and they run only after the first one.
Class lifecycle methods and their hook equivalents
Class components spell the lifecycle out as named methods. You will meet them in older code and in error boundaries, which still need a class.
| Class method | When it runs | Function component equivalent |
|---|---|---|
constructor | once, before the first render | useState(initial), useRef |
static getDerivedStateFromProps | before every render | compute the value during render, or reset with a key |
render | every render | the function body |
componentDidMount | after the first render is in the DOM | useEffect(fn, []) |
shouldComponentUpdate | before an update, can skip it | React.memo (it returns the opposite answer: "are props equal") |
getSnapshotBeforeUpdate | right before the DOM changes | no direct hook; useLayoutEffect with a ref is the closest |
componentDidUpdate | after each update | useEffect(fn, [deps]) (also runs after mount) |
componentWillUnmount | before removal | the cleanup returned by useEffect |
getDerivedStateFromError, componentDidCatch | when a child throws | none: write an error boundary class |
Here is a class that logs every method as it runs:
Mounting logs constructor, getDerivedStateFromProps, render, componentDidMount. A click logs getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, componentDidUpdate. Make shouldComponentUpdate return false and clicks stop updating the button: the state changes, but React skips the render. The class components page shows how to convert a class like this to a function.
Think in synchronization, not lifecycle
The hooks table is useful for reading old code, but it can lead you to write effects that copy class habits, such as one big "did mount" effect and one big "did update" effect. An effect is better read as "keep this outside thing in sync with these values": connect to roomId, and when roomId changes, disconnect and connect again. Write one effect per thing you synchronize, list the values it uses as dependencies, and let React decide when to run it.
Frequently Asked Questions
What are the phases of the React component lifecycle?
Mounting (React creates the component and puts its output on screen), updating (it renders again because its state, props or context changed) and unmounting (it is removed and cleans up).
What is the hook equivalent of componentDidMount?
useEffect(() => { ... }, []). The empty dependency array makes it run once, after the first render is on screen. The function it returns is the equivalent of componentWillUnmount.
What is the hook equivalent of componentDidUpdate?
useEffect with the values you care about in the dependency array, such as [userId]. Unlike componentDidUpdate, it also runs after the first render, so it covers mount and update together.
In what order do parent and child effects run?
React renders from the parent down, but runs effects from the children up: a child's effect runs before its parent's. On unmount, the parent's cleanup runs before the child's.
What are the render and commit phases?
In the render phase React calls your components to work out what the screen should look like; it can pause or throw this work away. In the commit phase it applies the changes to the DOM and runs layout effects; regular effects run after that, usually once the browser has painted.