Reactivity
wybthon.reactivity¶
reactivity
¶
Signal-based reactive primitives with an ownership tree.
This module is the heart of Wybthon's SolidJS-inspired reactivity. Every reactive computation (effect, memo) is owned by a parent scope. When that scope re-runs or is disposed, all child computations are torn down automatically, preventing leaks and giving lifecycle semantics that match component mount/unmount boundaries.
Core types:
Signal: mutable reactive container.Owner: base ownership scope (cleanups + children).Computation: a reactive computation that is itself an ownership scope.Resource: async data wrapper withdata/error/loading.
Public primitives:
create_signal: create a(getter, setter)pair.create_effect: auto-tracking side-effect.create_memo: auto-tracking derived value.on_mount/on_cleanup: lifecycle hooks.batch/untrack: scheduling control.create_root: spawn an independent reactive root.merge_props/split_props: prop composition helpers.map_array/index_array/create_selector: reactive list primitives.
Example
A counter with a derived doubled value::
from wybthon import create_signal, create_memo, create_effect
count, set_count = create_signal(0)
doubled = create_memo(lambda: count() * 2)
create_effect(lambda: print("doubled:", doubled()))
set_count(2) # logs "doubled: 4" on the next microtask flush
Classes:
| Name | Description |
|---|---|
Owner |
Base reactive ownership scope. |
Computation |
Reactive computation that tracks signals and re-runs when they change. |
Signal |
Mutable reactive container that notifies subscribed computations on change. |
ReactiveProps |
Reactive proxy over a component's props dict. |
Resource |
Async resource with reactive |
Functions:
| Name | Description |
|---|---|
read_prop |
Return the current value for |
iter_prop_keys |
Return the list of prop keys present on |
batch |
Batch signal updates so subscribers flush once at the end. |
untrack |
Run |
create_resource |
Create an async |
get_owner |
Return the current reactive owner scope, if any. |
run_with_owner |
Run |
create_signal |
Create a reactive signal and return |
create_effect |
Create an auto-tracking reactive effect. |
create_memo |
Create an auto-tracking computed value and return its getter. |
on_mount |
Register a callback to run once after the component mounts. |
on_cleanup |
Register a cleanup callback on the active reactive owner. |
children |
Resolve and memoize reactive children, returning a memo getter. |
get_props |
Return the |
on |
Create an effect with explicit dependencies. |
create_root |
Run |
merge_props |
Merge multiple prop sources into a reactive proxy. |
split_props |
Split a props source into groups by key name, plus a rest group. |
map_array |
Map a reactive list with stable per-item scopes (keyed by identity). |
index_array |
Map a reactive list with stable per-index scopes. |
create_selector |
Create an |
Owner
¶
Base reactive ownership scope.
Tracks child owners and cleanup callbacks. When disposed, children are disposed first (depth-first), then this owner's own cleanups run. This mirrors SolidJS's ownership model.
Attributes:
| Name | Type | Description |
|---|---|---|
_parent |
Optional[Owner]
|
Parent |
_children |
List[Owner]
|
List of child owners. |
_cleanups |
List[Callable[[], Any]]
|
Callbacks invoked LIFO during disposal. |
_disposed |
bool
|
True after |
_context_map |
Optional[Dict[int, Any]]
|
Lazily-allocated dict of context values stored at
this owner (used by |
Methods:
| Name | Description |
|---|---|
dispose |
Tear down this owner and all descendants. |
dispose
¶
Tear down this owner and all descendants.
Disposes children depth-first, then runs this owner's cleanup callbacks in LIFO order, and finally detaches from its parent. Subsequent calls are no-ops.
Computation
¶
Bases: Owner
Reactive computation that tracks signals and re-runs when they change.
Also serves as an ownership scope: child computations created during execution are disposed before each re-run, preventing leaks from conditionally-created effects.
Attributes:
| Name | Type | Description |
|---|---|---|
_fn |
The callback executed by |
|
_deps |
Set[Signal[Any]]
|
Set of signals this computation currently subscribes to.
Cleared and rebuilt on every |
Methods:
| Name | Description |
|---|---|
run |
Re-execute the tracked function, refreshing its dependency set. |
schedule |
Enqueue this computation for the next flush. |
dispose |
Dispose the computation and unsubscribe from all dependencies. |
run
¶
Re-execute the tracked function, refreshing its dependency set.
Disposes child owners and runs cleanups before each re-run so that conditional effects don't leak. The previous dependency edges are torn down and rebuilt while the body executes under this owner.
schedule
¶
Enqueue this computation for the next flush.
Called by Signal.set when a tracked dependency changes. When no
batch is active a microtask flush is scheduled; otherwise the
computation is held until the outermost batch completes.
dispose
¶
Dispose the computation and unsubscribe from all dependencies.
Removes this computation from any pending flush queue, clears dependency edges, and tears down child owners and cleanups.
Signal
¶
Signal(value: T, *, equals: Any = _DEFAULT_EQUALS)
Bases: Generic[T]
Mutable reactive container that notifies subscribed computations on change.
Most code uses create_signal which returns a
(getter, setter) tuple instead of exposing Signal instances directly.
This class is part of the public surface so Signal[T] can be used in
type hints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
T
|
The initial value. |
required |
equals
|
Any
|
Equality policy. See |
_DEFAULT_EQUALS
|
Methods:
| Name | Description |
|---|---|
get |
Return the current value and subscribe the active computation. |
set |
Write a new value and notify subscribers if it changed. |
get
¶
Return the current value and subscribe the active computation.
When called inside an effect, memo, or reactive hole, this signal is added to that computation's dependency set so it re-runs on future writes. Outside a tracking context the read is untracked.
Returns:
| Type | Description |
|---|---|
T
|
The current value held by the signal. |
set
¶
Write a new value and notify subscribers if it changed.
Equality is determined by the equals policy passed to the
constructor (default: is then ==, with equals=False to
bypass the check entirely).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
T
|
The new value to store. |
required |
ReactiveProps
¶
Reactive proxy over a component's props dict.
Every attribute or item access returns a reactive accessor: a
zero-arg callable that returns the current value and tracks the read.
This mirrors SolidJS's props.x semantics, adapted to Python.
Access patterns:
| Expression | Returns | Notes |
|---|---|---|
props.name |
callable getter | Stable across reads. |
props.name() |
current value | Tracked when called inside an effect or hole. |
props["name"] |
callable getter | Same as props.name. |
props.get("name", default) |
callable getter | Returns default when missing. |
props.value("name", default) |
current value | One-shot snapshot, with auto-unwrap. |
Embed the accessor directly in the VNode tree to create an automatic reactive hole; the surrounding DOM region updates only when the prop changes.
Example
Note
ReactiveProps is read-only. Parents and the reconciler update it
via the internal _update method.
Methods:
| Name | Description |
|---|---|
value |
Return the current value for |
get |
Return a callable getter for |
keys |
Return the prop names currently set on this instance. |
values |
Return current prop values as a list (tracked). |
items |
Return |
value
¶
Return the current value for key (tracked, with auto-unwrap).
If the stored prop value is a getter, it is invoked and the result
returned, mirroring _make_getter. If default is provided, it is
returned when key is absent from both the props dict and the
component's parameter defaults.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Prop name. |
required |
default
|
Any
|
Value returned when the key is missing. When omitted,
missing keys yield |
_MISSING
|
Returns:
| Type | Description |
|---|---|
Any
|
The current prop value (auto-unwrapped if it's a getter). |
get
¶
Return a callable getter for key, falling back to default if missing.
When key exists on the prop bag (in raw props, prior signals, or
component-parameter defaults), the returned getter reads the underlying
signal: a tracking scope subscribes to it.
When key is missing, the getter returns default on each call
without creating a tracked signal. This means repeated get(key, x)
/ get(key, y) calls each return their own default (no sticky
behavior). Components that need reactivity for a possibly-missing
prop should declare it as a parameter with a default value;
@component ensures a signal is created up front so future updates
always propagate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Prop name. |
required |
default
|
Any
|
Value returned by the fallback getter when |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[], Any]
|
A zero-arg callable. Subscribers to a missing-key getter are |
Callable[[], Any]
|
not notified when the prop later appears. |
Resource
¶
Bases: Generic[R]
Async resource with reactive data, error, and loading signals.
Wraps an awaitable fetcher and exposes signal-backed state so consumers
can render loading and error UIs declaratively (typically with
Suspense).
Use reload() to (re)fetch and cancel() to abort the in-flight
request.
When constructed with a source getter, the resource automatically
refetches when the source's tracked value changes (skipping the very
first read so it doesn't double-fetch on creation).
Attributes:
| Name | Type | Description |
|---|---|---|
data |
Signal[Optional[R]]
|
Reactive accessor for the most recent successful payload, or
|
error |
Signal[Optional[Any]]
|
Reactive accessor for the most recent exception, or |
loading |
Signal[bool]
|
Reactive accessor; |
Example
Methods:
| Name | Description |
|---|---|
reload |
Cancel any in-flight request and start a new fetch. |
cancel |
Abort the current in-flight fetch, if any. |
reload
¶
Cancel any in-flight request and start a new fetch.
Bumps the internal version, sets loading to True, clears
error, and dispatches the fetcher on the asyncio loop. Older
in-flight tasks are ignored when they resolve.
cancel
¶
Abort the current in-flight fetch, if any.
Calls AbortController.abort() on the wrapped browser controller,
cancels the asyncio task, and resets loading to False without
touching data or error.
signal
¶
signal(value: T) -> Signal[T]
Create a new Signal with the given initial value.
Most code should call create_signal instead;
this function exists for low-level uses that need the raw Signal
object (for example, attaching custom subscribers).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
T
|
The initial value stored in the signal. |
required |
Returns:
| Type | Description |
|---|---|
Signal[T]
|
A new |
read_prop
¶
Return the current value for key from props.
Works with both ReactiveProps (the common
case inside the reconciler) and plain dicts (test paths and a few
legacy call sites). When called inside a tracking scope, the read is
tracked against the underlying signal, mirroring the auto-unwrap
behavior of ReactiveProps.value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
props
|
Any
|
A |
required |
key
|
str
|
Prop name to read. |
required |
default
|
Any
|
Returned when |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
The current value, with auto-unwrap applied for |
iter_prop_keys
¶
Return the list of prop keys present on props.
Same uniform shim as read_prop for
components that iterate over an unknown prop bag (e.g. Link
forwarding extra attributes onto the rendered <a>).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
props
|
Any
|
A |
required |
Returns:
| Type | Description |
|---|---|
List[str]
|
A list of present keys, or an empty list when |
List[str]
|
dict-like. |
computed
¶
computed(fn: Callable[[], T]) -> _Computed[T]
Create a _Computed value derived from other signals.
Low-level helper used by create_memo. Most
code should use create_memo directly because it returns a plain
getter (matching SolidJS's API).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], T]
|
Zero-arg callable. Re-evaluated when any signal it reads changes. |
required |
Returns:
| Type | Description |
|---|---|
_Computed[T]
|
A |
effect
¶
effect(fn: Callable[[], Any]) -> Computation
Run a reactive effect immediately and return its Computation.
Low-level helper. Most code should use
create_effect which additionally supports
receiving the previous return value as an argument.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], Any]
|
Zero-arg callback. Re-runs when any signal it reads changes. |
required |
Returns:
| Type | Description |
|---|---|
Computation
|
The underlying |
on_effect_cleanup
¶
on_effect_cleanup(comp: Computation, fn: Callable[[], Any]) -> None
Register fn to run when comp is disposed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
comp
|
Computation
|
The computation whose disposal should trigger the cleanup. |
required |
fn
|
Callable[[], Any]
|
Zero-arg cleanup callback. |
required |
batch
¶
Batch signal updates so subscribers flush once at the end.
Two call shapes are supported:
- Context manager (Pythonic):
- Callback (SolidJS style):
When called with a function, the function's return value is returned.
Effects are flushed synchronously before batch returns, matching
SolidJS semantics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Optional[Callable[[], T]]
|
Optional zero-arg callable. When omitted, returns a context manager. |
None
|
Returns:
| Type | Description |
|---|---|
Union[T, _Batch]
|
Either a |
Union[T, _Batch]
|
return value of |
untrack
¶
untrack(fn: Callable[[], T]) -> T
Run fn without tracking any signal reads.
Useful inside effects when you need to read a signal without creating a dependency, or during component setup to seed local state from a prop without subscribing.
Inside untrack the dev-mode destructured-prop warning is also
silenced, so count, set_count = create_signal(untrack(initial))
cleanly opts out of the noise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], T]
|
Zero-arg callable to invoke with tracking suppressed. |
required |
Returns:
| Type | Description |
|---|---|
T
|
Whatever |
create_resource
¶
create_resource(source_or_fetcher: Union[Callable[[], Any], Callable[..., Awaitable[R]]], fetcher: Optional[Callable[..., Awaitable[R]]] = None) -> Resource[R]
Create an async Resource with cancellation support.
Can be called two ways:
create_resource(fetcher): simple fetcher, no source signal.create_resource(source, fetcher): refetches automatically when thesourcegetter's tracked value changes.
The fetcher should be an async function returning the data value. If it
accepts a signal keyword argument, an AbortSignal is passed for
cancellation support when running in a browser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_or_fetcher
|
Union[Callable[[], Any], Callable[..., Awaitable[R]]]
|
When called with one argument, this is the fetcher. When called with two, this is the source getter (typically a signal accessor). |
required |
fetcher
|
Optional[Callable[..., Awaitable[R]]]
|
Optional fetcher. Required when |
None
|
Returns:
| Type | Description |
|---|---|
Resource[R]
|
A |
Resource[R]
|
be read inside reactive scopes. |
get_owner
¶
Return the current reactive owner scope, if any.
Capture the owner before crossing async boundaries (e.g. await) and
restore it with run_with_owner to maintain
proper lifecycle management.
Returns:
| Type | Description |
|---|---|
Optional[Owner]
|
The active |
Optional[Owner]
|
scope. |
run_with_owner
¶
Run fn under the given ownership scope.
Useful for restoring context after an await boundary where the
reactive owner would otherwise be lost.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
owner
|
Optional[Owner]
|
The owner to install for the duration of |
required |
fn
|
Callable[[], T]
|
Zero-arg callable to execute. |
required |
Returns:
| Type | Description |
|---|---|
T
|
Whatever |
create_signal
¶
Create a reactive signal and return (getter, setter).
Works inside or outside components. Inside a stateful component the signal is captured by the render function's closure and persists naturally; there is no cursor system or "rules of hooks".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
T
|
Initial value stored in the signal. |
required |
equals
|
Any
|
Equality policy controlling when subscribers are notified. Accepts:
|
_DEFAULT_EQUALS
|
Returns:
| Type | Description |
|---|---|
tuple
|
A |
tuple
|
suitable for embedding as a reactive hole. |
create_effect
¶
create_effect(fn: Callable[..., Any]) -> Computation
Create an auto-tracking reactive effect.
The effect runs immediately and re-runs whenever any signal read inside
fn changes. on_cleanup may be called inside
fn to register per-run cleanup that runs before re-execution and on
disposal.
If fn accepts a positional parameter, the previous return value
is passed on each re-execution (None on the first run), matching
SolidJS's createEffect(prev => ...).
Inside a component, the effect is automatically disposed on unmount.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Any]
|
Zero- or one-arg callable. When it accepts an argument, the previous return value is forwarded. |
required |
Returns:
| Type | Description |
|---|---|
Computation
|
The underlying |
Computation
|
manually. |
create_memo
¶
Create an auto-tracking computed value and return its getter.
Re-computes only when signals read inside fn change. Inside a
component, the underlying computation is disposed on unmount.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], T]
|
Zero-arg callable producing the derived value. |
required |
Returns:
| Type | Description |
|---|---|
Callable[[], T]
|
A zero-arg callable. Reading it inside a tracking scope creates |
Callable[[], T]
|
a dependency on the memoised value. |
on_mount
¶
Register a callback to run once after the component mounts.
Must be called during a component's setup phase (the body of a
@component function, before the return).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], Any]
|
Zero-arg callback invoked once after the first render commits. |
required |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called outside a component setup phase. |
on_cleanup
¶
Register a cleanup callback on the active reactive owner.
Lifecycle differs by call site:
- Inside
create_effect: runs before each re-execution and on final disposal. - Inside a component's setup phase: runs when the component unmounts.
- Inside a reactive hole: runs before each hole re-evaluation and when the hole is disposed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], Any]
|
Zero-arg cleanup callback. |
required |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called outside any reactive scope. |
children
¶
Resolve and memoize reactive children, returning a memo getter.
Wraps a getter that returns children (e.g. lambda: props.children())
and returns a memo that flattens nested lists and unwraps callables.
Matches SolidJS's children() helper.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], Any]
|
Zero-arg getter that returns the raw children value
(typically |
required |
Returns:
| Type | Description |
|---|---|
Callable[[], List[Any]]
|
A zero-arg memo getter producing a flat list of resolved children. |
get_props
¶
Return the ReactiveProps proxy for the current component.
The returned object provides reactive access to individual props. Each attribute / index lookup yields a callable getter; call the getter to read the current value (tracked when called inside an effect or hole).
Most components should rely on the destructured parameters provided
by @component. Use get_props() for advanced cases such as generic
wrappers, key iteration, or proxy-mode interop.
Returns:
| Type | Description |
|---|---|
'ReactiveProps'
|
The cached |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called outside a component setup phase. |
on
¶
on(deps: Union[Callable[[], Any], List[Callable[[], Any]]], fn: Callable[..., Any], defer: bool = False) -> Computation
Create an effect with explicit dependencies.
deps may be a single getter or a list of getters. fn receives the
current value(s) as positional arguments. Only the listed deps are
tracked; the body of fn runs inside untrack.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
deps
|
Union[Callable[[], Any], List[Callable[[], Any]]]
|
One getter or a list of getters to subscribe to. |
required |
fn
|
Callable[..., Any]
|
Callback receiving the current dep value(s) on each change. |
required |
defer
|
bool
|
When |
False
|
Returns:
| Type | Description |
|---|---|
Computation
|
The underlying |
create_root
¶
Run fn inside an independent reactive root.
Useful for spawning long-lived reactive work that should not be tied to the surrounding component's lifecycle (e.g., global stores).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[Callable[[], None]], T]
|
Callable receiving a |
required |
Returns:
| Type | Description |
|---|---|
T
|
Whatever |
merge_props
¶
merge_props(*sources: Any) -> '_MergedProps'
Merge multiple prop sources into a reactive proxy.
Each source may be a plain dict, a zero-arg getter that returns a
dict, or another _MergedProps / _SplitProps. Later sources
override earlier ones on key conflicts.
The returned object supports dict-like access ([], .get, in,
iteration). When a source is a callable, it is called on each property
access, so signal reads inside the getter are tracked by the current
reactive computation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*sources
|
Any
|
One or more prop sources, in priority order (rightmost wins). |
()
|
Returns:
| Type | Description |
|---|---|
'_MergedProps'
|
A reactive merged-props proxy. |
split_props
¶
Split a props source into groups by key name, plus a rest group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
props
|
Any
|
A |
required |
*key_groups
|
List[str]
|
One or more lists of keys defining each group. |
()
|
Returns:
| Type | Description |
|---|---|
Any
|
A tuple |
...
|
reactive proxy that lazily reads from the original |
Tuple[Any, ...]
|
final |
Tuple[Any, ...]
|
group. |
map_array
¶
map_array(source: Callable[[], Optional[List[Any]]], map_fn: Callable[[Callable[[], Any], Callable[[], int]], T]) -> Callable[[], List[T]]
Map a reactive list with stable per-item scopes (keyed by identity).
Items are matched by reference identity. The mapping callback runs once per unique item; when an item leaves the source list, its reactive scope is disposed automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Callable[[], Optional[List[Any]]]
|
Zero-arg getter that returns the current list (typically a signal accessor). |
required |
map_fn
|
Callable[[Callable[[], Any], Callable[[], int]], T]
|
Called as |
required |
Returns:
| Type | Description |
|---|---|
Callable[[], List[T]]
|
A zero-arg getter producing the mapped list. Reading it inside a |
Callable[[], List[T]]
|
reactive scope subscribes to source changes. |
index_array
¶
index_array(source: Callable[[], Optional[List[Any]]], map_fn: Callable[[Callable[[], Any], int], T]) -> Callable[[], List[T]]
Map a reactive list with stable per-index scopes.
Unlike map_array, scopes are keyed by index
position. Each slot has a reactive item_getter signal that
updates when the value at that index changes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Callable[[], Optional[List[Any]]]
|
Zero-arg getter that returns the current list. |
required |
map_fn
|
Callable[[Callable[[], Any], int], T]
|
Called as |
required |
Returns:
| Type | Description |
|---|---|
Callable[[], List[T]]
|
A zero-arg getter producing the mapped list. |
create_selector
¶
Create an O(1) selection signal.
When source() changes, only computations that previously called the
returned is_selected(key) with the old or new key are
notified, instead of every subscriber re-running.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Callable[[], Any]
|
Zero-arg getter returning the currently-selected key. |
required |
Returns:
| Type | Description |
|---|---|
Callable[[Any], bool]
|
A function |
Callable[[Any], bool]
|
selection changes. |
Ownership classes¶
Owner¶
Base reactive ownership scope. Tracks child owners and cleanup callbacks.
| Attribute | Description |
|---|---|
_parent |
Parent Owner or None for roots. |
_children |
List of child Owner instances. |
_cleanups |
Callbacks run on disposal (LIFO order). |
_context_map |
Optional dict mapping context IDs to values (used by Provider). |
| Method | Description |
|---|---|
dispose() |
Dispose children depth-first, run own cleanups, detach from parent. |
_lookup_context(ctx_id, default) |
Walk up the owner chain to find a context value. |
_set_context(ctx_id, value) |
Store a context value on this owner. |
Computation(Owner)¶
Reactive computation that tracks Signal reads and re-runs when they
change. Also an ownership scope: child computations created during
execution are disposed before each re-run.
| Method | Description |
|---|---|
run() |
Dispose children and cleanups, clear deps, re-execute the function under _current_owner = self. |
schedule() |
Enqueue a re-run on the next flush. |
dispose() |
Cancel subscriptions, clear deps, remove from pending queue, run cleanups. |
Public API¶
Signals-first API (recommended)¶
create_signal(value, *, equals=...) -> (getter, setter). Optionalequals: default uses value equality (==) with an identity (is) fast-path;equals=Trueis equivalent to the default;equals=Falsenotifies on everyset();equals=fnwithfn(old, new) -> boolskips notification whenfnreturnsTrue(custom comparator). Useequals=lambda a, b: a is bfor SolidJS-style identity-only semantics.create_effect(fn) -> Computation. The returnedComputationis added as a child of the current owner. Inside a component's setup phase the owner is the_ComponentContext(effect survives re-renders, disposed on unmount). Inside a render function the owner is the renderComputation(effect disposed on re-render). Supports previous value:create_effect(lambda prev: ...).create_memo(fn) -> getter. Creates aComputationunder the current owner; disposed when the owner is disposed.on_mount(fn). Run after first render.on_cleanup(fn). Appendsfnto the current owner's cleanup list. Insidecreate_effect: runs before each re-execution and on disposal. Inside a component's setup phase: runs when the component unmounts.batch() -> context managerorbatch(fn) -> result. The callback form flushes synchronously.
create_signal and equals¶
from wybthon import create_signal
# Default: value equality (==) with an identity (is) fast-path.
# Re-setting an unchanged value is a no-op; a new container with
# value-equal contents also skips. Mutating the same list/dict in
# place and re-setting the same reference is a no-op too -- copy
# the container first or pass ``equals=False`` to force notification.
x, set_x = create_signal({"a": 1})
# Equivalent to the default.
y, set_y = create_signal(0, equals=True)
# Always notify subscribers, even when the value is unchanged.
z, set_z = create_signal(0, equals=False)
# SolidJS-style identity-only semantics: notify whenever the new
# reference is not the same Python object as the old.
w, set_w = create_signal([], equals=lambda old, new: old is new)
ReactiveProps and get_props()¶
get_props() returns the ReactiveProps proxy for the current
@component instance. The proxy exposes one consistent shape for
every prop:
props.name(attribute) orprops["name"](item) returns a stable zero-arg accessor. Calling the accessor reads the current value (tracked when called inside an effect or hole). Embedding it in a VNode tree creates a reactive auto-hole.props.value(name, default=None)reads the current value immediately (a one-shot, untracked-friendly snapshot).- The proxy supports
in,len(), iteration, and==against dicts.
from wybthon import component, create_effect, dynamic, get_props, p
@component
def Greeting(name="world"):
props = get_props()
create_effect(lambda: print("name is now", props.name()))
return p(dynamic(lambda: f"Hello, {props.name()}!"))
When a component declares a single positional parameter with no
default, the decorator passes the proxy in directly (proxy mode);
otherwise each parameter is bound to its own accessor and there is no
need to call get_props().
ReactiveProps is read-only; the parent/reconciler updates underlying
values. When a parent passes a getter (e.g., name=my_signal), the
proxy unwraps it transparently, and children always read with props.name().
get_owner() and run_with_owner(owner, fn)¶
After an await, the reactive owner stack may no longer match the component that started the work. Capture the owner before awaiting and restore it when creating effects or other scoped work:
from wybthon import create_effect, get_owner, run_with_owner
async def load():
owner = get_owner()
data = await fetch_something()
run_with_owner(owner, lambda: create_effect(lambda: use(data)))
children(fn)¶
children(getter) wraps a zero-argument callable that returns the children value (often lambda: get_props().children()) and returns a memo getter that flattens and resolves the list. Matches Solid's children() helper. Import under an alias (e.g., from wybthon import children as resolve_children) if your component also names a parameter children.
from wybthon import children, component, dynamic, get_props, h3, section
@component
def Card(title=""):
props = get_props()
resolved = children(lambda: props.children())
return section(h3(dynamic(lambda: props.title())), *resolved(), class_="card")
Resources¶
create_resource(fetcher) -> Resourcecreate_resource(source, fetcher) -> Resource. Refetches when the source changes.
Reactive utilities¶
untrack(fn). Run without tracking signal reads.on(deps, fn, defer=False). Effect with explicit deps.create_root(fn). Creates an independentOwnerroot.fnreceives adisposecallback that tears down the root and all its children. Effects created inside the root are owned by it and cleaned up ondispose().merge_props(*sources). Merge prop sources into a reactive proxy. Each source may be a plaindict, a callable getter, or another proxy. Reads are lazy: callable sources are called on each access for signal tracking. Returns an object supporting[],.get(),in,len(), iteration, and==comparison with dicts.split_props(props, *key_groups). Split a props source into reactive proxy groups by key name, plus a rest group. Returns(group1, ..., rest); each proxy lazily reads from the original source.
Reactive list primitives¶
map_array(source, map_fn). Keyed reactive list mapping.sourceis a getter returning a list;map_fn(item_getter, index_getter)runs once per unique item (matched by reference identity). Returns a getter producing the mapped list. Per-item reactive scopes are created and disposed automatically.index_array(source, map_fn). Index-keyed reactive list mapping. Likemap_arraybut keyed by index position.map_fn(item_getter, index: int): the item getter is a signal that updates in place. Returns a getter producing the mapped list.create_selector(source). Efficient selection signal. Returnsis_selected(key) -> bool. When the source changes, only the previous and new key's dependents re-run (O(1) instead of O(n)).
Global state¶
A single _current_owner global tracks the active ownership scope.
When a Computation.run() executes, it sets _current_owner = self so
that any effects or memos created during execution become its children.
Type hints are provided for all public functions and classes.