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. - Provider elements (
type == "__Provider__"), which push and pop context values during tree traversal. - Error boundary elements (
type == "__ErrorBoundary__"), which catch exceptions in child subtrees and render a fallback. - Key-based child reconciliation with indexed, move-aware inserts (keyed reorders emit one move per child instead of detach-all / re-attach-all).
- Post-render effect flushing. After each commit, all queued effects are executed so they see the committed native tree.
- 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, error boundaries) delegate the tag
of their rendered subtree 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,
- flushes pending effects (so they see the committed tree), and
- runs the layout pass, emitting only changed frames.
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. |
compute_layout_for_test |
Build and compute a layout tree without touching the backend. |
mount
¶
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. The node is re-rendered on the next
flush_dirty
pass, which the screen host schedules. 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 |
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.