VNode
wybthon.vnode¶
vnode
¶
Virtual node data structure and tree-building helpers.
This module defines the core VNode type and the
functions used to build it (h, Fragment,
memo, dynamic). It is intentionally
free of browser or DOM dependencies, so VNode trees can be constructed
and inspected anywhere CPython runs.
A _dynamic VNode (created via dynamic or implicitly
when a zero-argument callable appears in a child position) represents a
reactive hole: the reconciler wraps the getter in its own effect that
updates only the corresponding DOM region when the getter's dependencies
change. This is the building block for SolidJS-style "setup once, update
fine-grained" rendering.
Example
Building a small subtree without a browser::
from wybthon import h, Fragment
view = h("section", {"class": "card"},
h("h1", {}, "Hello"),
Fragment(h("p", {}, "Body 1"), h("p", {}, "Body 2")))
Classes:
| Name | Description |
|---|---|
VNode |
Virtual node representing an element, text, component, or reactive hole. |
Functions:
| Name | Description |
|---|---|
dynamic |
Create a reactive-hole VNode that re-evaluates |
is_getter |
Return True when |
h |
Create a VNode from a tag, props, and children. |
Fragment |
Group multiple children without adding an extra DOM wrapper element. |
memo |
Wrap a function component to skip re-mounts when its props are unchanged. |
VNode
¶
VNode(tag: Optional[Union[str, Callable[..., Any]]], props: Optional[PropsDict] = None, children: Optional[List[ChildType]] = None, key: Optional[Union[str, int]] = None)
Virtual node representing an element, text, component, or reactive hole.
Uses __slots__ for a compact memory layout and faster attribute
access, which is meaningful when authoring large lists. Internal
attributes (el, subtree, render_effect, component_ctx,
_frag_end) are populated by the reconciler when the VNode is
mounted.
Attributes:
| Name | Type | Description |
|---|---|---|
tag |
Element tag name ( |
|
props |
Mapping of prop names to values. Event handlers, attributes, and reactive accessors all live here. |
|
children |
List of child |
|
key |
Optional stable identity used for keyed list reconciliation. |
to_text_vnode
¶
dynamic
¶
Create a reactive-hole VNode that re-evaluates getter on dependency changes.
This is the explicit form of the same machinery that wraps callable
children automatically. Use it when you want to be explicit about
which child is dynamic, or to attach a stable key for keyed reuse
inside a fragment.
The getter may return a VNode, a str, a list of either, or None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
getter
|
Callable[[], Any]
|
Zero-arg callable evaluated inside its own effect. Any signal reads inside the getter become dependencies that trigger re-evaluation. |
required |
key
|
Optional[Union[str, int]]
|
Optional stable identity used by keyed reconciliation. |
None
|
Returns:
| Type | Description |
|---|---|
VNode
|
A |
is_getter
¶
Return True when value is a zero-arg callable suitable for a reactive hole.
The check excludes:
VNodeinstances- Classes (
isinstance(value, type)) - Components and providers (marked with
_wyb_component/_wyb_provider) Refobjects (have acurrentattribute)- Callables that require positional arguments (e.g. event handlers taking an event object)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Any value, typically a child or prop value being normalized. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
flatten_children
¶
Flatten nested child lists into a single list, dropping None entries.
normalize_children
¶
Normalize a mixed list of children into a flat list of VNodes.
Per-element handling:
VNode: kept as-is. Fragments are flattened into the parent list.- Zero-arg callable: wrapped in a
_dynamicVNode (reactive hole). - Anything else: coerced to a text VNode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
children
|
List[ChildType]
|
Children as produced by |
required |
Returns:
| Type | Description |
|---|---|
List[VNode]
|
A flat list of |
h
¶
h(tag: Optional[Union[str, Callable[..., Any]]], props: Optional[PropsDict] = None, *children: Any) -> VNode
Create a VNode from a tag, props, and children.
This is the low-level VNode constructor used everywhere. For
common HTML tags, prefer the helpers in
wybthon.html (div, span, button, …).
Callable children (zero-argument getters) are passed through
unchanged; normalize_children wraps them as _dynamic VNodes when
the parent element mounts. Components receive their children verbatim
via the children prop so they can decide how to render them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tag
|
Optional[Union[str, Callable[..., Any]]]
|
An HTML tag name ( |
required |
props
|
Optional[PropsDict]
|
Mapping of prop names to values. May be |
None
|
*children
|
Any
|
Children to attach. Lists/tuples are flattened. |
()
|
Returns:
| Type | Description |
|---|---|
VNode
|
A new |
Fragment
¶
Group multiple children without adding an extra DOM wrapper element.
Fragments use empty comment nodes as start/end markers and mount their
children directly into the parent container. This avoids extra
elements that would pollute selectors like :first-child or affect
layout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Either a sequence of children ( |
()
|
Returns:
| Type | Description |
|---|---|
VNode
|
A |
memo
¶
memo(component: Callable[..., Any], are_props_equal: Optional[Callable[[PropsDict, PropsDict], bool]] = None) -> Callable[..., Any]
Wrap a function component to skip re-mounts when its props are unchanged.
Because Wybthon component bodies run once, memo is only useful
when you want to skip re-mounting on a prop change (for example,
components with an expensive setup phase). Most ordinary components
do not need it; fine-grained holes already minimise DOM work.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component
|
Callable[..., Any]
|
The component callable to memoize. |
required |
are_props_equal
|
Optional[Callable[[PropsDict, PropsDict], bool]]
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
Callable[..., Any]
|
A wrapped component callable. Identity is preserved across |
Callable[..., Any]
|
renders so the reconciler can detect "same component, same |
Callable[..., Any]
|
props". |
What's in this module¶
vnode defines the framework's lightweight virtual DOM node type along
with a few pure helpers for marker nodes (Fragment) and reactive holes
(dynamic, is_getter).
A VNode is just a Python object with three properties:
| Field | Description |
|---|---|
tag |
A string tag ("div", "button"), a component callable, or a special marker. |
props |
A dict of attributes, event handlers, and reserved props (children, key, ref). |
children |
A list of child VNodes, primitives, or callables for reactive holes. |
You usually create VNodes via h or the helpers in
wybthon.html rather than instantiating VNode
directly.
See also¶
vdom: render and patch primitives.reconciler: keyed list and child diffing.- Concepts: Virtual DOM