skip to content

The Render Phase

What actually makes React re-render a component — a state update, a new context value, or a parent re-rendering — and why the render function has to stay pure. Interviewers ask this to catch the belief that props are 'pushed' into a child or that rendering itself touches the DOM.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In React, what actually causes a component's function to run again? Candidates often answer "when its props change" — say what really schedules a re-render and why props are not an independent trigger.

level: juniorimportance: must knowfreq 82%

answer

  1. three triggers, not four
  2. props are pulled, never pushed
  3. own state, read context, parent ran
  4. identical props still re-run the child
  5. refs and module variables notify nobody

basics

~20 s

React re-runs a component when its own state changes, when a context it reads receives a new value, or when its parent re-renders. Props changing is not a separate trigger — it is a consequence of the parent running again.

solid answer

~50 s

There are three things that make React call a component function again. First, its own state changed — a setter from `useState` or `useReducer` was called. Second, a context it reads with `useContext` (or `use`) was given a new value by its provider. Third, its parent re-rendered, because rendering a parent produces the elements describing its children and React keeps rendering down into them. A fourth, rarer one is an explicit `root.render(...)` on the root created by `createRoot`. "Props changed" is not on the list: props are just arguments the parent computed while rendering, so a child can only see new props because the parent already re-ran. That is also why a child re-renders when the parent does even if every prop value is identical — skipping that is an explicit optimization, not the default.

code

javascript · 16 lines
javascript
import { useState } from 'react';

function Child({ label }) {
  console.log('Child rendered with', label);
  return <span>{label}</span>;
}

export default function Parent() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <Child label="never changes" />
    </div>
  );
}

go deeper

for a junior

Name the three triggers out loud — own state, a context you read, or the parent re-rendering — and say plainly that props changing is a result of the parent rendering, not a cause.

for a middle

Be ready to explain why identical props still re-run a child, and to list what deliberately does not schedule a render: ref writes, mutations of state objects, and module-level variables.

for a senior

Show that you use this to reason about real render storms: identify which component owns the state that fired, then which subtree that pulls in, before reaching for any optimization.

for a principal

Own the architectural consequence — where state lives determines how much of the tree re-renders, so placing state near its consumers is a structural decision, not a micro-optimization applied after profiling.

## What "render" means here Rendering in React is React calling your component function and receiving a description of what the UI should look like — a tree of React elements. It is not a DOM update. A render can happen and result in zero DOM writes, so "my component re-rendered" and "the browser repainted that node" are different statements. Keeping those apart is most of what this question tests. ## The triggers **1. Its own state changed.** Calling the setter returned by `useState`, or the `dispatch` returned by `useReducer`, marks that component as needing work. React schedules a re-render of that component. (If the new value is the same as the current one React may skip the work — the details of that bail-out are the update-queue's business, not the trigger list's.) **2. A context it reads got a new value.** A component that calls `useContext(ThemeContext)` — or reads the context with `use(ThemeContext)` in React 19 — is subscribed to that context. When the nearest provider above it renders with a different `value`, every consumer of that context re-renders, even consumers that would otherwise have been skipped. **3. Its parent re-rendered.** This is the one people forget. When React re-runs a parent, the JSX the parent returns describes its children; React continues the render into those children by default. It does not compare the child's props first and decide whether the call is worth it. So a purely presentational child with constant props still runs again whenever its parent runs. **4. The root was re-rendered.** Calling `root.render(<App />)` again on the root returned by `createRoot` starts a render from the top. Applications rarely do this after startup. ## Why "props changed" is not a trigger Props are ordinary function arguments. The parent computes them while it is rendering and passes them into the child element it creates. Nothing pushes a prop into a mounted child from the outside; there is no channel for that. The only way a child can observe a different prop value is for the parent to have produced a new element with a new value — which means the parent rendered. So "props changed" is downstream of trigger 3, never a cause of its own. The practical consequence catches people out in both directions: - Same props, parent rendered → the child **does** re-render. - A value the child closes over changed outside React (a module variable, a mutable object) → the child does **not** re-render, because nothing told React. ## What does not trigger a render - Assigning to `ref.current`. Refs are deliberately outside the render trigger system; that is their purpose. - Mutating an object or array that is already in state, without calling the setter. React never sees it. - Mutating a module-level variable, a class instance, or `window`. - Changing a DOM node directly. - An external store changing, unless the component subscribed to it — that is what `useSyncExternalStore` exists for. ## Where the render starts and where it stops When a deeply nested component updates its own state, React starts the work at that component and walks down through the tree it returns. Ancestors and siblings are not re-run. So a `setState` in a leaf does not re-render the whole application — a very common misconception, usually voiced as "React re-renders everything on every state change". What it does re-render, by default, is that component's entire returned subtree. ```jsx function Page() { const [q, setQ] = useState(''); // typing here re-runs Page and everything Page returns, // including <Sidebar /> whose props never change return ( <> <input value={q} onChange={(e) => setQ(e.target.value)} /> <Sidebar /> <Results query={q} /> </> ); } ``` ## Why the default is affordable Re-running a component function is usually cheap: it allocates plain objects and returns them. React then compares that output with the previous one and only the actual differences are applied to the DOM in the commit that follows. The expensive parts of a web page — layout, paint, DOM mutation — are proportional to what changed, not to how many component functions ran. That is the trade React makes, and it is why the framework can afford to re-render a subtree instead of tracking which individual value each child depends on.

  • If a parent re-renders and passes a child exactly the same prop values, does the child's function still run?
    Yes. React does not compare props before calling a child; re-rendering the returned subtree is the default. Skipping that work is an explicit opt-in optimization you apply deliberately, and it costs a comparison of its own, so the default of just re-running the function is usually the right one.
  • Does assigning a new value to ref.current cause a re-render?
    No. Refs are a mutable box that lives across renders precisely because writing to them is invisible to React — nothing is scheduled. That is why refs suit values the UI does not display, like a timer id or a DOM node, and why a value the user must see belongs in state instead.
  • A component deep in the tree calls its own setState. Which components re-render?
    That component and the tree it returns. React begins the work at the owner of the changed state and walks downward; its parent, its siblings, and everything above are untouched unless they are also part of that returned subtree. "React re-renders the whole app on every update" is simply wrong.

saying these in an interview costs you the question

  • Says a component re-renders whenever its props change
  • Believes React compares a child's props before calling it
  • Claims every setState re-renders the entire application
  • Thinks re-rendering means the DOM was updated
  • Expects mutating a state object to trigger a render

context

open as a page

React requires that rendering a component be pure. What does purity mean for a component body concretely, which operations violate it, and what breaks if you ignore the rule?

level: middleimportance: must knowfreq 66%

basics

~20 s

A pure component body computes its output from props, state and context alone, changing nothing that existed before it ran. Mutating props or state, writing to shared variables, reading or writing the DOM, and starting requests during render all violate it.

open as a page

React splits an update into a render phase and a commit phase. What work does React do during the render phase, and what does it deliberately leave out of it?

level: middleimportance: must knowfreq 64%

basics

~20 s

The render phase calls component functions, builds the new element tree, and works out what changed — all in memory, touching nothing outside React. Applying those changes to the DOM, attaching refs, and running effects all belong to the commit that follows.

open as a page

A React component calls a state setter directly in its body rather than in an event handler or effect. Depending on whose state it is, the app either throws "Too many re-renders. React limits the number of renders to prevent an infinite loop." or logs a warning about updating a component while rendering a different one. Explain what React is doing in each case.

level: seniorimportance: should knowfreq 44%

basics

~20 s

Updating your own state during render makes React re-run that component immediately without committing; unguarded, it loops until React aborts with "Too many re-renders". Updating a different component's state during render is never valid and React warns, because that component already rendered in this pass.

open as a page