Reconciler¶
Diffs successive virtual 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 the
NativeViewRegistry.
Virtual-tree reconciler with a batched, tag-based commit protocol.
Maintains a tree of VNode objects
(each owning an integer tag that identifies its native view) and
diffs incoming Element trees to compute the
minimal set of native mutations.
The diff phase is pure: it never touches the native layer. Each pass
appends ops (pythonnative.mutations) to a transaction list, and the
commit applies them through a single
backend.apply_mutations(ops) call. Event callbacks never cross into
the native layer at all; they live in the Python-side
EventRegistry, keyed by tag, so
re-renders that only produce fresh closures cost nothing natively.
Supports:
- Native elements (
typeis a string like"Text"). - Function components (
typeis a callable decorated withcomponent). Their hook state is preserved across renders. Components may return a single element, a list of elements, orNone; every node in the tree can contribute zero or more native views to its native parent (multi-child rendering). - Provider elements (
type == "__Provider__"), which push and pop context values during traversal and, when their value changes, re-render every descendant that read the context on its last render (reactive context), even under memoized components that skipped. - Error boundary elements (
type == "__ErrorBoundary__"), which catch exceptions in child subtrees, invokeon_error, render a fallback (optionally receiving aresetcallable), and remount their children whenresetis called. - Fragments (
type == "__Fragment__"), expanded inline unless keyed, in which case they participate in keyed reconciliation as a transparent multi-child wrapper. - Portals (
type == "Portal"), native elements whose handler hosts their children in a top-level overlay. Portals contribute no child to their native parent; their subtree is laid out against the viewport like aModal. - Key-based child reconciliation with indexed, move-aware inserts
computed by simulating the native child list, so appends and keyed
reorders emit the minimal set of
InsertOps. - Two effect phases: layout effects
(
use_layout_effect) flush synchronously after mutations and layout; passive effects (use_effect) flush afterwards. - Incremental layout: a parallel
LayoutNodetree is cached across passes; clean subtrees keep their cached nodes (enabling the layout engine's measurement memo) and only frames that actually changed are sent to the native side.
Classes:
| Name | Description |
|---|---|
VNode |
A mounted |
Reconciler |
Create, diff, and patch native view trees from |
Functions:
| Name | Description |
|---|---|
next_tag |
Allocate a fresh, process-unique view tag. |
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 (function
components, providers, boundaries, keyed fragments)
delegate the tag of their first native root. |
|
native_view |
Any
|
The platform-native view object, resolved from the
registry after commit. May be |
children |
Ordered list of child |
|
parent |
Optional[VNode]
|
The owning |
hook_state |
Any
|
The component's
|
mounted |
bool
|
|
Reconciler
¶
Reconciler(backend: Any)
Create, diff, and patch native view trees from Element descriptors.
After each mount,
reconcile, or
flush_dirty
pass the reconciler:
- applies the accumulated mutation ops in one batch,
- resolves freshly created native views and populates refs,
- runs the layout pass, emitting only changed frames,
- flushes layout effects (children-first), then
- flushes passive effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backend
|
Any
|
An object implementing the registry protocol
( |
required |
Methods:
| Name | Description |
|---|---|
mount |
Build native views from |
reconcile |
Diff |
root_view |
Return the current root native view, or |
root_tag |
Return the root native view's tag, or |
unmount |
Destroy the entire mounted tree and release native views. |
dispatch_command |
Run an imperative command against the view registered under |
mark_dirty |
Queue |
flush_dirty |
Re-render only the component subtrees marked dirty since the last pass. |
set_viewport_size |
Update the viewport size and re-run layout if it changed. |
register_back_handler |
Register a back-press handler; returns an unregister callable. |
dispatch_back_press |
Offer the system back action to registered handlers. |
reset_hook_signatures |
Forget recorded hook-order signatures across the whole tree. |
compute_layout_for_test |
Build and compute a layout tree without touching the backend. |
mount
¶
Build native views from element and return the root native view.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
element
|
Element
|
The root |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The platform-native view that represents the root of the |
Any
|
mounted tree (the first native root when the root element |
Any
|
renders several). |
reconcile
¶
dispatch_command
¶
Run an imperative command against the view registered under tag.
mark_dirty
¶
mark_dirty(vnode: VNode) -> None
Queue vnode (a function component) for a local re-render.
Called by a component's use_state / use_reducer setter
when its own state changes, and by reactive context when a
Provider's value changes for a consumer that a memoized
ancestor would otherwise skip. The node is re-rendered on the
next flush_dirty
pass. Marking is idempotent and cheap; the actual render is
deferred so several setters (e.g. inside
batch_updates) coalesce into a
single pass.
flush_dirty
¶
flush_dirty() -> Any
Re-render only the component subtrees marked dirty since the last pass.
This is the hot path for state-driven updates: instead of
re-running the whole app from the root, each dirty function
component re-runs its own body (reusing its
HookState) and reconciles just
its subtree. Nodes are processed shallowest-first so that when a
dirty ancestor's re-render already covers a dirty descendant, the
descendant is skipped (its _dirty flag is cleared by the
ancestor pass). The whole batch commits as one native
transaction.
Returns:
| Type | Description |
|---|---|
Any
|
The (possibly replaced) root native view, so the host can |
Any
|
re-attach it if the root changed. |
set_viewport_size
¶
Update the viewport size and re-run layout if it changed.
Called by the screen host whenever the platform reports a new
container size (Android: onLayoutChange; iOS:
viewDidLayoutSubviews). The first call after mount
triggers the initial layout pass; subsequent identical
sizes are no-ops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
float
|
Viewport width in points. |
required |
height
|
float
|
Viewport height in points. |
required |
register_back_handler
¶
Register a back-press handler; returns an unregister callable.
Used by use_back_handler.
Handlers are dispatched most-recently-registered first.
reset_hook_signatures
¶
Forget recorded hook-order signatures across the whole tree.
Called by the hot-reload machinery after a Fast Refresh swaps in new component bodies, since the old call signatures no longer apply.
compute_layout_for_test
¶
compute_layout_for_test(viewport_width: float, viewport_height: float) -> Optional[LayoutNode]
Build and compute a layout tree without touching the backend.
Test helper that returns the synthetic viewport LayoutNode
with all descendants positioned. Returns None if no tree
has been mounted yet.
Next steps¶
- Read the high-level walkthrough in Reconciliation.
- See how native widgets are registered in Native views.