Skip to content

Reconciler

wybthon.reconciler

reconciler

Reconciliation engine: mounting, patching, and unmounting VNode trees.

This module translates VNode trees into batched DOM operations. It never touches the DOM directly: every mutation is emitted as a compact op against an integer node id (see wybthon.kernel), and the whole buffer is applied in a single bridge crossing at commit time (end of render, end of each effect flush).

Mental model:

  • Components run once. A function component is invoked a single time during mount. Its returned VNode tree is mounted directly. Reactive updates flow through reactive holes embedded in that tree, not by re-running the component body.
  • Reactive holes are _dynamic VNodes whose getter is re-evaluated by an effect when its dependencies change. They're created automatically whenever a callable child or callable prop value appears in the tree, and explicitly via dynamic.
  • Components return a VNode (or a value coercible to one). The idiomatic style is to return a static tree and use dynamic for explicit reactive subtrees; a returned zero-arg callable is also accepted and is wrapped in a single reactive hole for convenience (handy when authoring higher-order components).

Public surface:

  • render: top-level entry point.
  • mount: emit ops creating DOM for a new VNode under a parent node id.
  • unmount: tear down a VNode and its DOM.
  • patch: diff two VNodes and emit the difference.

Functions:

Name Description
render

Render a VNode tree into a container element.

mount

Emit ops mounting a VNode (or string) under the node parent_id.

unmount

Unmount vnode, disposing its effects, ownership scope, and DOM.

patch

Diff old against new and emit minimal DOM ops under parent_id.

render

render(vnode: VNode, container: Union[Element, str, int]) -> Element

Render a VNode tree into a container element.

Subsequent calls with the same container patch the existing tree in place; only the differences are applied. All emitted DOM ops are committed to the backend in one batch before this returns.

Parameters:

Name Type Description Default
vnode VNode

The root VNode to render.

required
container Union[Element, str, int]

An Element wrapper, a CSS selector string identifying an existing DOM node, or a kernel node id.

required

Returns:

Type Description
Element

The wrapped container Element. Useful for chaining or for

Element

retaining a reference to the mount point.

Example
from wybthon import h, render

render(h("h1", {}, "Hello, world!"), "#app")

mount

mount(vnode: Union[VNode, str], parent_id: int, anchor_id: Optional[int] = None) -> None

Emit ops mounting a VNode (or string) under the node parent_id.

When the VNode carries an owner_scope (set by For/Index for cached rows), mounting runs under that reactive owner so the row's effects survive later list updates.

Parameters:

Name Type Description Default
vnode Union[VNode, str]

The VNode to mount. Strings are coerced to text VNodes.

required
parent_id int

Kernel id of the parent node.

required
anchor_id Optional[int]

Optional id of the sibling node to insert before. When None, the new nodes are appended to the parent.

None

unmount

unmount(vnode: VNode) -> None

Unmount vnode, disposing its effects, ownership scope, and DOM.

Calls cleanup on the owning component context (if any), removes delegated event handlers, runs on_cleanup callbacks, and removes the subtree's DOM. The removal itself is a handful of ops (one REMOVE per top-level node plus one RELEASE for the whole subtree), applied in a single commit.

Parameters:

Name Type Description Default
vnode VNode

The VNode to tear down. Safe to call on already-unmounted nodes (becomes a no-op).

required

patch

patch(old: Optional[VNode], new: VNode, parent_id: int) -> None

Diff old against new and emit minimal DOM ops under parent_id.

Identical VNode instances (old is new, e.g. cached For rows) are skipped entirely. Same-type VNodes are patched in place (props and children diffed); different types are unmounted and remounted at the same position.

Parameters:

Name Type Description Default
old Optional[VNode]

The previously-rendered VNode, or None for the initial mount.

required
new VNode

The new VNode to render.

required
parent_id int

Kernel id of the node that directly contains this VNode's DOM.

required

What's in this module

The reconciler walks a previous and next VDOM tree and emits the minimal set of DOM operations to bring the page into sync. It never touches the DOM directly: every mutation is a compact op against an integer node id (see wybthon.kernel), applied in one bridge crossing per commit. It handles keyed lists, fragments, components, text nodes, and reactive holes.

Most users never call into this module directly. render mounts a tree and the reconciler kicks in for subsequent updates. Read this page if you're contributing to Wybthon, debugging a diffing bug, or curious how holes plug into the patching loop.

Key responsibilities

Concern How the reconciler handles it
Mounting Static subtrees mount through the template fast path: one clone op per mount of a registered skeleton, instead of one op per node.
Element diffing Matches by tag. If tags differ, the old subtree unmounts.
Children A three-pass O(n) match (identity, then key, then type) with a longest-increasing-subsequence move pass keeps DOM moves minimal.
Components A component's body runs once; the reconciler updates props on the existing component instance.
Reactive holes Each hole is an effect; the reconciler patches only the affected region when the signal updates.
Cleanup Unmounting disposes the owner recursively and retires the whole subtree with one REMOVE per top-level node plus one RELEASE op.

See also