Skip to content

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 (type is a string like "Text").
  • Function components (type is a callable decorated with component). Their hook state is preserved across renders. Components may return a single element, a list of elements, or None; 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, invoke on_error, render a fallback (optionally receiving a reset callable), and remount their children when reset is 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 a Modal.
  • 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 LayoutNode tree 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 Element plus its native identity.

Reconciler

Create, diff, and patch native view trees from Element descriptors.

Functions:

Name Description
next_tag

Allocate a fresh, process-unique view tag.

VNode

VNode(element: Element, children: List[VNode], tag: Optional[int] = None)

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 Element last rendered into this slot.

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. None when the subtree renders no native view.

native_view Any

The platform-native view object, resolved from the registry after commit. May be None for purely virtual wrappers that rendered nothing.

children

Ordered list of child VNode instances. Wrappers may own any number of children; each child contributes zero or more native roots to the nearest native ancestor.

parent Optional[VNode]

The owning VNode, or None for the tree root. Used by local (component-scoped) re-renders to bubble a changed subtree up to the nearest native container.

hook_state Any

The component's HookState when the node wraps a function component, otherwise None.

mounted bool

False once the node has been destroyed, so stale entries in the reconciler's dirty set are skipped.

Reconciler

Reconciler(backend: Any)

Create, diff, and patch native view trees from Element descriptors.

After each mount, reconcile, or flush_dirty pass the reconciler:

  1. applies the accumulated mutation ops in one batch,
  2. resolves freshly created native views and populates refs,
  3. runs the layout pass, emitting only changed frames,
  4. flushes layout effects (children-first), then
  5. flushes passive effects.

Parameters:

Name Type Description Default
backend Any

An object implementing the registry protocol (apply_mutations, resolve_view, measure_intrinsic, command). PythonNative ships Android, iOS, and desktop registries; tests can pass a registry stocked with mock handlers.

required

Methods:

Name Description
mount

Build native views from element and return the root native view.

reconcile

Diff new_element against the current tree and patch native views.

root_view

Return the current root native view, or None before mount.

root_tag

Return the root native view's tag, or None before mount.

unmount

Destroy the entire mounted tree and release native views.

dispatch_command

Run an imperative command against the view registered under tag.

mark_dirty

Queue vnode (a function component) for a local re-render.

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

mount(element: Element) -> Any

Build native views from element and return the root native view.

Parameters:

Name Type Description Default
element Element

The root Element to render.

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

reconcile(new_element: Element) -> Any

Diff new_element against the current tree and patch native views.

Parameters:

Name Type Description Default
new_element Element

The desired root element after a state change.

required

Returns:

Type Description
Any

The (possibly replaced) root native view.

root_view

root_view() -> Any

Return the current root native view, or None before mount.

root_tag

root_tag() -> Optional[int]

Return the root native view's tag, or None before mount.

unmount

unmount() -> None

Destroy the entire mounted tree and release native views.

dispatch_command

dispatch_command(tag: Optional[int], name: str, args: Optional[Dict[str, Any]] = None) -> Any

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

set_viewport_size(width: float, height: float) -> None

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_back_handler(handler: Callable[[], bool]) -> Callable[[], None]

Register a back-press handler; returns an unregister callable.

Used by use_back_handler. Handlers are dispatched most-recently-registered first.

dispatch_back_press

dispatch_back_press() -> bool

Offer the system back action to registered handlers.

Returns:

Type Description
bool

True if a handler consumed the event (the platform

bool

should not run its default behavior).

reset_hook_signatures

reset_hook_signatures() -> None

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_tag

next_tag() -> int

Allocate a fresh, process-unique view tag.

Next steps