Reconciler¶
Diffs successive element trees and applies the smallest set of native
mutations that bring the on-screen view tree in line with the new
description. The reconciler is platform-agnostic; it talks to native
widgets exclusively through a backend such as the
BridgeBackend
or the FakeBackend tests use.
Renders are batched automatically (one flush per callback, effect, or
task step), and a flush that re-schedules itself more than fifty times
raises RuntimeError("Too many re-renders") through the nearest
ErrorBoundary.
Reconciler package: turns element trees into batched native mutations.
The public surface is Reconciler and
VNode; the remaining modules are
implementation detail:
core: render, diff, commit.boundaries:ErrorBoundaryandSuspense.layout_pass: publishing the frames the renderer core lays out.children: minimal move planning for keyed child lists.vnode: the mounted-tree node and pure helpers.
Core¶
The Reconciler: element trees in, native mutations out.
One reconciler owns a mounted application tree or headless test render. Logical screens, overlays, and mounted list rows share that tree. Each pass runs the same phases in order:
- Render: component bodies run for the subtrees that need it,
producing
Elementdescriptions. Hook state is installed for the duration of each body. - Diff and stage: elements are compared with the mounted
VNodein their slot. Creates, prop updates, attach/detach, and destroys are staged into an op list instead of being applied immediately. - Commit: the staged ops go to the backend in one batch, refs are
populated, the layout pass runs (once a viewport is known), then
effects flush:
use_layout_effectfirst,use_effectafter. An effect that raises is routed to the nearestErrorBoundary, exactly like a render-time exception.
State setters never render inline. request_render
schedules one flush on the application loop with call_soon, so every
setter call within one callback, effect, or task step coalesces into one
pass on every host. A render requested while a pass is in flight (an
effect setting state, a boundary reset) is drained before the pass
returns, so hosts never observe a half-committed tree. A flush that keeps
re-scheduling itself more than MAX_RENDER_PASSES times raises
RuntimeError("Too many re-renders") from the component that keeps
dirtying itself, routed through the nearest boundary like any render
error.
The class is assembled from mixins to keep each concern readable:
this module holds the render/diff/commit pipeline,
boundaries handles
ErrorBoundary and Suspense, and
layout_pass runs flexbox.
Classes:
| Name | Description |
|---|---|
Reconciler |
Owns one mounted tree and translates element diffs into native mutations. |
Attributes:
| Name | Type | Description |
|---|---|---|
MAX_RENDER_PASSES |
How many times one flush may re-schedule itself before it is a render storm. |
MAX_RENDER_PASSES
module-attribute
¶
How many times one flush may re-schedule itself before it is a render storm.
A component that sets state unconditionally in an effect, or a setter
called on every render, keeps the tree dirty forever. After this many
drain iterations inside one flush the reconciler raises
RuntimeError("Too many re-renders") from the offending component
and routes it through the nearest ErrorBoundary.
Reconciler
¶
Reconciler(backend: Any)
Bases: BoundaryMixin, LayoutMixin
Owns one mounted tree and translates element diffs into native mutations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backend
|
Any
|
An object implementing the backend protocol
( |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
backend |
The backend passed at construction. |
|
on_render_requested |
Optional[Callable[[], None]]
|
Optional callback hosts set to be told a
flush is due (to hop onto the UI thread or guard with a red
box). It runs on the application loop, one turn after the
first state change that scheduled it. When |
on_back_registered |
Optional[Callable[[], None]]
|
Optional callback fired when the first
|
transitions |
Deferred-render queue behind
|
Methods:
| Name | Description |
|---|---|
root_view |
The root native view object, or |
mount |
Build native views for |
reconcile |
Diff |
flush_dirty |
Re-render only the components whose state changed, then commit. |
unmount |
Tear down the mounted tree, running effect cleanups and destroying native views. |
dispatch_command |
Run an imperative command against the view registered under |
dispatch_back_press |
Offer the system back action to registered handlers, newest first. |
walk |
Yield every mounted node in depth-first, document order. |
mark_dirty |
Queue |
request_render |
Schedule one flush on the application loop (automatic batching). |
register_back_handler |
Register a back-press handler; returns an unregister callable. |
defer_input |
Queue state writes during mounting, preserving their functional order. |
wait_for_commit |
Wait until mounting and its queued state updates have settled. |
set_viewport_size |
Update the viewport size and re-run layout if it changed. |
root_tag
property
¶
Tag of the root native view, or None when nothing is mounted.
commit_pending
property
¶
commit_pending: bool
Whether native acknowledgement is holding the surface's next render.
viewport_size
property
¶
The last viewport size supplied by the host, (0, 0) before layout is possible.
mount
¶
Build native views for element and return the root native view.
Any previously mounted tree is unmounted first.
reconcile
¶
Diff element against the mounted tree and patch native views.
Pending state updates are drained as part of the pass, so hosts only call this when the root element itself changed (new props from outside the tree).
flush_dirty
¶
flush_dirty() -> Any
Re-render only the components whose state changed, then commit.
This is the hot path for state-driven updates: each dirty component re-runs its own body and reconciles its subtree in place; the batch commits as one native transaction. Returns the (possibly replaced) root native view.
unmount
¶
Tear down the mounted tree, running effect cleanups and destroying native views.
dispatch_command
¶
Run an imperative command against the view registered under tag.
dispatch_back_press
¶
dispatch_back_press() -> bool
Offer the system back action to registered handlers, newest first.
Returns True if a handler consumed the event (the platform
should not run its default behavior).
mark_dirty
¶
mark_dirty(vnode: VNode) -> None
Queue vnode (a component) for a local re-render on the next flush.
request_render
¶
Schedule one flush on the application loop (automatic batching).
Never renders inline: every request made during one callback,
effect, or task step lands in the same call_soon flush.
Requests made while a pass is in flight are drained by that
pass instead.
register_back_handler
¶
Register a back-press handler; returns an unregister callable.
defer_input
¶
Queue state writes during mounting, preserving their functional order.
wait_for_commit
async
¶
Wait until mounting and its queued state updates have settled.
Mounted nodes¶
Mounted-tree nodes and the pure helpers the reconciler shares.
VNode pairs an
Element with the identity of the native view
it produced (its tag) plus the bookkeeping the reconciler needs
across passes (layout cache, boundary state, hook state).
Everything in this module is side-effect free with respect to the native layer.
Classes:
| Name | Description |
|---|---|
VNode |
A mounted |
Functions:
| Name | Description |
|---|---|
next_tag |
Allocate a fresh, process-unique view tag. |
normalize_children |
Normalize arbitrary render output into a flat list of Elements. |
VNode
¶
A mounted Element plus its native identity.
The reconciler walks parallel trees of VNode and incoming
Element to compute the minimal set of native mutations.
Attributes:
| Name | Type | Description |
|---|---|---|
element |
The |
|
tag |
Integer identity of the underlying native view. Native
elements own a fresh tag; transparent wrappers (components,
providers, boundaries, keyed fragments) delegate the tag of
their first native root. |
|
native_view |
Any
|
The platform-native view object, resolved from the
backend after commit. |
children |
List[VNode]
|
Ordered list of child |
parent |
Optional[VNode]
|
The owning |
hook_state |
Optional[HookState]
|
The component's
|
mounted |
bool
|
|
Methods:
| Name | Description |
|---|---|
depth |
Return the number of ancestors above this node (the root has depth 0). |
normalize_children
¶
Normalize arbitrary render output into a flat list of Elements.
Accepts a single element, None, True/False (both
skipped, enabling inline conditionals like cond and Text(...)),
lists/tuples/generators (flattened recursively), and unkeyed
Fragments (expanded inline so they never touch the native tree).
Keyed Fragments are preserved so they can participate in keyed
reconciliation as a unit.
Non-Element values other than the above are dropped with a dev-mode warning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
children
|
Any
|
The value to normalize. |
required |
owner
|
str
|
Label of the element or component the children belong to, for diagnostics. |
''
|
dynamic
|
bool
|
Whether |
False
|
Keyed children¶
Pure planning of native child-list moves.
Given the tags a native container currently holds and the tags it
should hold after a pass, plan_child_moves
returns the minimal sequence of (tag, index) insertions (with
move-aware insert_child semantics: a child already attached is
re-positioned) that transforms one into the other.
The algorithm keeps the longest increasing subsequence of surviving
children in place and moves only the rest, processing right to left so
every moved child is inserted directly before an already-settled
neighbor. That yields n - len(LIS) operations, the optimum for a
move-aware insert primitive.
Functions:
| Name | Description |
|---|---|
longest_increasing_subsequence |
Return the indices (into |
plan_child_moves |
Compute |
longest_increasing_subsequence
¶
Return the indices (into values) of one longest strictly increasing subsequence.
plan_child_moves
¶
Compute (tag, index) insert ops turning before into after.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
before
|
Sequence[int]
|
Tags currently attached to the container, in order.
Tags absent from |
required |
after
|
Sequence[int]
|
Desired child tags, in order. Tags absent from
|
required |
Returns:
| Type | Description |
|---|---|
List[Tuple[int, int]]
|
Ordered insert operations. Applying each as "detach |
List[Tuple[int, int]]
|
attached, then insert at |
Next steps¶
- Read the high-level walkthrough in Reconciliation.
- See the backend the reconciler commits to in Native views.