skip to content

Update Cycle & Reconciliation

How a described output becomes committed host nodes: when an update runs, how the runtime finds what changed, and how it applies it. Most UI performance and list bugs live in this pipeline.

on this pageshow

explore

questions

page 1 of 2

After a framework has worked out what changed, what does the commit pass do, and why does it run uninterrupted?

level: juniorimportance: must knowfreq 62%

answer

  1. apply, not decide
  2. one pass, nothing half-applied
  3. mutate, then references, then callbacks
  4. deferred effects come after paint

basics

~20 s

Commit is the apply step. It writes the already-computed changes into the host tree — creating, updating, moving and removing nodes, then wiring references and listeners — as one pass, so nobody ever sees a half-applied screen.

solid answer

~40 s

An update has two halves. The first half is pure bookkeeping: run or re-evaluate the output-producing code, compare it with what is currently on screen, and collect the work to do. Nothing is visible yet. The second half, the commit, performs that work against the host: insert new nodes, write the attributes and text that differ, move reordered children, remove and tear down what is gone, populate references, attach or detach listeners. The commit is driven to completion in one pass — even in runtimes that can pause and resume the compute half — because a partially applied tree is a screen a user or other code could actually observe, and because two components updated from the same event must land together. Ordered callback tiers hang off the end of it.

go deeper

for a junior

Remember the two halves: first the framework works out what should change, then the commit writes it to the real nodes. Effects and callbacks come after the write, not before.

for a middle

Be able to list what the commit does — insert, write changed properties, move, remove and tear down, populate references, wire listeners — and name the ordered tiers that hang off it.

for a senior

Explain why the pass is atomic: a paused commit is an observable half-updated screen, and callbacks that depend on references or on children being mounted need a single ordering authority.

for a principal

Frame it as the boundary where a speculative, discardable computation becomes irreversible host state, and argue for keeping expensive work out of the pre-paint window as a budget the whole app shares.

## Two halves of one update Every component framework separates **deciding** from **applying**. The deciding half produces a *description* of the intended output — a tree of node descriptors, or, in a fine-grained runtime, a set of dirty bindings — and compares it with what the runtime believes is currently committed. The result is a list of host operations. None of it touches the screen. The **commit** is the applying half: the moment the runtime stops reasoning about the new output and starts mutating the host. It is where the framework's abstraction finally meets a real tree of nodes. Why the split exists at all: - The compute half is **speculative**. It may be thrown away (a newer update arrives, an error propagates to a boundary, the runtime decides to yield). Throwing away a description costs nothing; un-applying host writes costs a visible flicker. - The compute half can be **chunked** across tasks. The commit cannot, because the intermediate states would be observable. - Ordering guarantees the rest of the framework depends on — references populated before user callbacks run, children mounted before parents' callbacks — only make sense if one authority applies everything in a known order. ## What the commit actually performs - **Creation and insertion** of host nodes for parts of the tree that did not exist, inserted before the correct sibling so document order matches the description. - **Property writes** for nodes that are kept: only the attributes, styles and text whose values differ from the previous description. - **Moves** for children that reordered, which is a reinsertion of an existing node rather than a rebuild. - **Removal and teardown** for nodes and instances that are gone: detach listeners, clear references, run cleanup for the subtree, drop the instance state. - **Reference population**, so code that needs the concrete host node has one to read. - **Listener wiring**, either per node or through a root container, depending on the framework's strategy. - **Queueing the callback tiers** that run at defined points around the write. ## The ordered sub-phases Most runtimes commit in roughly this order: 1. **Pre-removal cleanup** — run teardown for instances about to disappear, while their nodes are still in place. 2. **Mutations** — insert, write, move, remove. 3. **References** — point the reference slots at the nodes that now exist. 4. **Layout-sensitive callbacks** — run synchronously, still inside the commit, before the host has had a chance to present the new frame. 5. **The host paints** the committed tree. 6. **Deferred effects** — the ordinary side-effect tier, run after the frame is on screen. | Step | Who runs it | Can the user see the state before it? | |---|---|---| | Produce a description | the framework's compute half | no — nothing is applied | | Compare against the committed tree | the framework's compute half | no | | Mutate host nodes | the commit | not mid-pass; the pass is atomic to the user | | Layout-sensitive callbacks | the commit, before paint | no | | Deferred effects | after paint | yes — the frame is already visible | ## What must not happen during it - **Long synchronous work.** Everything in the commit sits between the update and the next frame; a slow callback in the pre-paint tier delays the pixels. - **Triggering a fresh synchronous update from inside the pass.** Runtimes tolerate it by re-entering with another pass, but it doubles the work before paint and is a common cause of an update loop. - **Assuming the host is quiet.** Other code, a third-party widget, or the platform's own parser fixes may have changed nodes the framework believes it owns; the commit writes what its own bookkeeping says, not what it re-reads. ## Where frameworks differ The apply-versus-decide split is universal, but its size is not. A runtime that re-runs the output-producing function on every change builds a large work list and commits it in one pass. A runtime with fine-grained dependency tracking wires each dynamic value to the exact node at create time, so an update's `commit` is one or two direct writes with almost no list to walk. A compile-time runtime emits those writes ahead of time, so the `commit` is a generated function. In all three the distinction still holds: something computes the new value, and something else writes it to the host.

  • Why can a runtime interrupt the compute half of an update but not the commit?
    The compute half only builds a description and a work list — abandoning it leaves no trace, so it can be chunked across tasks or thrown away when a newer update arrives. The commit mutates real nodes, so stopping halfway leaves a tree that is part old and part new: visible to the user and readable by any other code on the page.
  • Where do a component's side-effect callbacks run relative to the host write?
    After it. The commit mutates nodes and populates references first, then runs the layout-sensitive tier synchronously before the host paints, and the ordinary deferred tier after the frame is on screen. That is why a reference is reliably populated inside any of those callbacks, and why slow work belongs in the deferred tier.

saying these in an interview costs you the question

  • Thinks the commit is where the framework decides what changed
  • Says component code re-runs during the commit to produce output
  • Expects side-effect callbacks to run before the host nodes are written
  • Believes the runtime pauses mid-write and resumes in a later task
  • Assumes one component's write lands independently of its siblings
open as a page

A runtime re-describes a whole screen on each state change, yet only one text node changes — what happens in between?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Describing the output is cheap in-memory work. The runtime then decides which parts of that description differ from the previous update and turns only those differences into host operations, so one text write reaches the document.

open as a page

In a component framework, what is an error boundary, and what happens when a component below it throws while rendering?

level: juniorimportance: must knowfreq 72%

basics

~20 s

An error boundary is an ancestor that catches a throw raised while the runtime renders or commits its subtree, renders a fallback in that subtree's place, and leaves the rest of the application mounted and usable.

open as a page

When a component framework re-renders a list of children, what does giving each child a key taken from the data do?

level: juniorimportance: must knowfreq 84%

basics

~20 s

A key tells the runtime which previously rendered child each new child is. Without one it pairs children by position, so inserting, removing or reordering items leaves per-item state, focus and host nodes behind at the old slot.

open as a page

In a component framework, what is the difference between describing output with a compiled template and with a render function?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A template is declarative markup with its own syntax for expressions, conditionals and loops, compiled ahead of time into render code. A render function returns the same description from ordinary code, using the host language's control flow.

open as a page

Why do component frameworks coalesce several state writes made during one event into a single update pass?

level: juniorimportance: must knowfreq 70%

basics

~10 s

So the user never sees a half-updated screen and the work is paid for once. The runtime marks what changed, lets the event's code finish, then runs one pass that covers every write.

open as a page

How does a renderer's per-node work differ when it creates a first mount, updates a live tree, or claims existing markup?

level: middleimportance: must knowfreq 58%

basics

~20 s

Create builds and inserts every node and writes every property. Update mutates only what differs, plus moves and removals. Claim-existing walks markup produced elsewhere, reuses those nodes untouched, and only attaches listeners, references and instance state.

open as a page

When is skipping a subtree because its inputs compare equal actually cheaper than re-running it?

level: middleimportance: must knowfreq 60%

basics

~20 s

A skip pays a comparison on every update and saves the subtree's pass only when the inputs match. It wins above a large, rarely-changing subtree, and loses on cheap ones or when a caller rebuilds the inputs each update.

open as a page

Which failures does a render-time error boundary not catch, and what catches those instead?

level: middleimportance: must knowfreq 62%

basics

~20 s

A boundary covers only throws raised while the runtime produces or commits output. A throw in an event handler, a timer callback or a rejected promise escapes to explicit handling and the page's global error and unhandled-rejection listeners.

open as a page

When a list matched by position has a middle item removed, which per-item state ends up on the wrong row and why?

level: middleimportance: must knowfreq 76%

basics

~20 s

Everything not recomputed from inputs: the row's own state, focus and caret, an uncontrolled field's text, scroll offset and running animations. Positional matching re-feeds each instance with the next item's data and destroys the last instance, not the removed one's.

open as a page

What can a template compiler know about a compiled template that a build step cannot reliably learn from a render function?

level: middleimportance: must knowfreq 64%

basics

~20 s

A template's whole structure is visible before it runs, so a compiler can separate constant markup from dynamic parts, hoist the constants to be created once, record which binding reads which value, and reject an impossible binding at build time.

open as a page

Why does reading state back right after writing it give the old value in some component frameworks and the new one in others?

level: middleimportance: must knowfreq 74%

basics

~20 s

Two write models. A runtime that re-runs components treats each pass's state as a fixed snapshot, so a queued write cannot change the value that pass already read. A fine-grained runtime updates the cell immediately and merely schedules dependents.

open as a page

The first client pass over server-rendered markup finds nodes that do not match its description. Which code causes that, and how do you fix it?

level: seniorimportance: must knowfreq 60%

basics

~20 s

Four classes cause it: a value only one side can read, a clock or random source, locale- or timezone-dependent formatting, and parser-corrected markup. Fix it by rendering the volatile part after attach, marking the subtree unmatched, or repairing the nesting.

open as a page

A collapsible panel's form loses everything typed into it whenever the panel is collapsed and reopened. Why, and what are the options?

level: seniorimportance: must knowfreq 58%

basics

~20 s

A conditional that leaves the subtree out of the described output unmounts it: instance state, effects and host nodes are destroyed, so reopening builds a fresh form. Either hide it instead, or hold the values above the conditional.

open as a page

Why can a component return several sibling elements without a wrapper, and what is rendered when it returns nothing?

level: juniorimportance: should knowfreq 55%

basics

~20 s

A fragment groups siblings in the description without producing a host node, so a component can return several nodes while adding none. Returning nothing renders no output at all, yet the component instance still exists and keeps its state and effects.

open as a page

What changes for an application when a framework attaches one listener at a root container instead of one per rendered node?

level: middleimportance: should knowfreq 48%

basics

~20 s

Listener count stops scaling with node count, and inserts and moves cost no wiring, because the framework listens once at a root and finds handlers in its own tree. The cost is ordering and interop with hand-written listeners.

open as a page

How does a runtime that only checks marked subtrees differ in per-update work from one that checks every component?

level: middleimportance: should knowfreq 52%

basics

~20 s

A marked design visits only the region a state write flagged, so unmarked branches cost nothing, but state owned high flags everything below it. A whole-tree check visits every component and re-evaluates every binding regardless.

open as a page

When error boundaries are nested and a component throws during render, which one catches the throw and what does it replace?

level: middleimportance: should knowfreq 55%

basics

~10 s

The nearest ancestor declaring a handler catches it, so an inner boundary wins over an outer one. It replaces the whole subtree it wraps with its fallback, not just the failing component.

open as a page

What properties must a list key have to be usable, and which common key sources fail them?

level: middleimportance: should knowfreq 66%

basics

~20 s

A key must come from the data, stay the same for an item across updates, be unique among its siblings, and compare cheaply. The index, a value generated while rendering, a duplicated field and an editable field each fail one.

open as a page

When a component interpolates a value as text, what does the framework guarantee, and what changes with the raw-markup opt-in?

level: middleimportance: should knowfreq 66%

basics

~20 s

Interpolated values are set as text, so markup characters stay literal and a value can never add an element or a handler. The raw opt-in drops that guarantee: the string is parsed as markup, so it must be trusted or sanitised.

open as a page

Why must the function that produces a component's output be pure, and what breaks when it is not?

level: middleimportance: should knowfreq 64%

basics

~20 s

Because the runtime decides how often it runs: it may run it again for the same state, throw the result away, or skip it. Side effects inside it therefore happen an unpredictable number of times.

open as a page

A component measures a node after an update and writes a corrected position from it, and users see a one-frame flash. Why?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The measure-and-write ran in the callback tier that fires after the host presented the frame, so the uncorrected position was painted once and the correction became a second visual state. The pre-paint tier inside the commit avoids it.

open as a page

Dragging one row to the end of a 500-row list stalls the interface, though only that row moved — what is the update paying for?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Three counts hide behind one move: descriptions produced, comparisons performed, host operations applied. Relocating a row is one or two host operations, but the pass still compares all 500 siblings and re-enters every row whose inputs were rebuilt.

open as a page

When a boundary reports a caught render error to monitoring, why is a stack trace alone rarely enough to locate the failure?

level: seniorimportance: should knowfreq 44%

basics

~20 s

A trace names the functions on the stack, which for a render are mostly runtime internals plus one component type that may be mounted in dozens of places. The component path is what locates the failure.

open as a page

A caught error shows a fallback with a retry action. What must that reset actually do so the subtree does not fail again immediately?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Clearing the boundary's caught state is not enough: the reset must change what produced the throw — re-run the failed work with fresh inputs, or remount the subtree under a new identity so no stale state survives.

open as a page

A detail panel keeps the previous record's unsaved edits when a new record is selected; how does changing its key fix that, and what does it cost?

level: seniorimportance: should knowfreq 58%

basics

~20 s

The panel sits in the same tree position, so it is paired with the same instance and only its inputs refresh. Keying it by the record id makes each record a different child, discarding the old instance and its subtree.

open as a page

A state write reveals a panel and the next line measures it and finds nothing - why, and what is the correct fix?

level: seniorimportance: should knowfreq 58%

basics

~20 s

The write only queued an update, so the host tree still holds the previous output when the next line runs. Move the read into the runtime's after-update signal, or force a synchronous flush for that one read.

open as a page

Your team wants one component codebase to drive a browser document, a native view tree and a server-side string. How would you split the runtime, and where does that abstraction leak?

level: principalimportance: should knowfreq 32%

basics

~20 s

Keep identity, instance state, scheduling, difference-finding and commit ordering host-independent, and push a small primitive surface — create, update, insert, remove, text — into a per-host adapter. It leaks at property vocabulary, events, measurement and the component ecosystem.

open as a page

How would you decide where subtree-skip boundaries belong across a large application, and what would you ask the team to stop doing?

level: principalimportance: should knowfreq 44%

basics

~10 s

Start from what the runtime must visit per update for this app's shape, then gate only where a measurement shows a large, rarely-changing region below changing state. Stop blanket-gating and prefer moving state down.

open as a page

How would you decide where error boundaries belong across a long-lived application, and which failures would you deliberately leave uncontained?

level: principalimportance: should knowfreq 40%

basics

~20 s

Put a boundary where a region can fail on its own and the page is still worth using without it; leave it out where a fallback would strand the user or make a wrong page look complete.

open as a page

showing 1–30 of 33