Create a node for a Convex paginated query subscription.
Convex client used for the subscription.
Convex paginated query function reference.
Query arguments, initial page size, and optional initial state.
Protected ReadonlyclientConvex client used by this node.
Latest subscription error.
Runtime options for this Retree node.
Latest structured query result.
Latest paginated query state emitted by Convex.
Dependencies to listen for changes to.
When any IReactiveDependency criteria is met, a change will be emitted for this ReactiveNode instance.
Keep this getter deterministic. Do not start subscriptions, perform network work, or mutate state here. Use ReactiveNode.onObserved, ReactiveNode.onUnobserved, and ReactiveNode.onChanged for lifecycle work.
The returned array may change length or ordering while the node is
observed. Retree treats added, removed, or reordered entries as
invalidation and refreshes subscriptions. Use null when you want an
inactive slot to keep its position, but it is not required for
correctness.
ProtectedactionCreate a typed action function bound to this node's Convex client.
Convex action function reference.
A typed action function.
Creates a new IReactiveDependency instance.
the node to listen to "nodeChanged" events for.
Optionalcomparisons: any[]Optional. Values to compare between updates to node.
dependency object.
Use this inside the ReactiveNode.dependencies getter or an
@select dependency selector when one slot needs explicit comparison
cells. If node is a Retree-managed object, it is observed with
nodeChanged. If node is a primitive or unproxied value, Retree
treats it as a comparison-only dependency.
Comparison cells should be deterministic. If their length/order changes,
Retree treats that as invalidation and emits for this node. If no
comparisons are provided, every nodeChanged event from the dependency
emits for this node.
Creates a new IReactiveDependency instance.
dependency object.
Use this inside the ReactiveNode.dependencies getter or an
@select dependency selector when one slot needs explicit comparison
cells. If node is a Retree-managed object, it is observed with
nodeChanged. If node is a primitive or unproxied value, Retree
treats it as a comparison-only dependency.
Comparison cells should be deterministic. If their length/order changes,
Retree treats that as invalidation and emits for this node. If no
comparisons are provided, every nodeChanged event from the dependency
emits for this node.
Create a reactive pointer to an existing Retree-managed node.
Existing Retree-managed node to point at.
A Retree-managed RetreeLink whose current points at node.
This is a convenience wrapper around Retree.link. Use it when a
ReactiveNode method needs to return or store a pointer to a node owned
elsewhere without reparenting that node.
Do not use link when ownership should move; use Retree.move or
ReactiveNode.moveTo. Do not use it when two locations need
independent state; use Retree.clone.
Request more items for the active paginated query.
Number of additional items to load.
Whether Convex started a load-more request.
ProtectedmemoMemoize the result of fn, scoped to this ReactiveNode instance.
Optionalcomparisons: unknown[]Two forms:
this.memo(fn, deps?) — derives the cache key
from the active getter's property name. Throws if called outside a getter or
more than once in the same getter.this.memo(key, fn, deps?) — works anywhere; required when
stacking multiple memo cells in one getter, or memoizing inside a method.Cache semantics for comparisons:
undefined: run fn under automatic dependency trapping and
recompute when one of the trapped reads changes.[]: compute once and cache forever for this instance.[a, b, ...]: recompute when any cell shallow-changes using Object.is.
Tree-node cells are compared by their latest reproxy identity, so passing
this.list correctly invalidates when list mutates.memo is a cache, not a subscription. It does not emit
nodeChanged or trigger React renders by itself. Pair it with
dependencies, Retree.select, or useSelect when you also need
notification behavior.
class ListFilter extends ReactiveNode {
list: Card[] = [];
searchText = "";
// Keyless form
get filteredList() {
return this.memo(
() => this.list.filter((c) => c.text === this.searchText),
[this.list, this.searchText]
);
}
// Explicit-key form (e.g. when stacking two memos in one getter)
get pair() {
const a = this.memo("a", () => expensiveA(), [this.list]);
const b = this.memo("b", () => expensiveB(), [this.searchText]);
return { a, b };
}
get dependencies() { return [this.dependency(this.list)]; }
}
Memoize the result of fn, scoped to this ReactiveNode instance.
Optionalcomparisons: unknown[]Two forms:
this.memo(fn, deps?) — derives the cache key
from the active getter's property name. Throws if called outside a getter or
more than once in the same getter.this.memo(key, fn, deps?) — works anywhere; required when
stacking multiple memo cells in one getter, or memoizing inside a method.Cache semantics for comparisons:
undefined: run fn under automatic dependency trapping and
recompute when one of the trapped reads changes.[]: compute once and cache forever for this instance.[a, b, ...]: recompute when any cell shallow-changes using Object.is.
Tree-node cells are compared by their latest reproxy identity, so passing
this.list correctly invalidates when list mutates.memo is a cache, not a subscription. It does not emit
nodeChanged or trigger React renders by itself. Pair it with
dependencies, Retree.select, or useSelect when you also need
notification behavior.
class ListFilter extends ReactiveNode {
list: Card[] = [];
searchText = "";
// Keyless form
get filteredList() {
return this.memo(
() => this.list.filter((c) => c.text === this.searchText),
[this.list, this.searchText]
);
}
// Explicit-key form (e.g. when stacking two memos in one getter)
get pair() {
const a = this.memo("a", () => expensiveA(), [this.list]);
const b = this.memo("b", () => expensiveB(), [this.searchText]);
return { a, b };
}
get dependencies() { return [this.dependency(this.list)]; }
}
Move this node to a new structural parent.
Retree-managed destination collection or object.
Optionalkey: numberOptional array insertion index, map key, or object property key.
The latest reproxy for this node after it moves.
This is a convenience wrapper around Retree.move. Use it from instance methods when a node should transfer ownership to another Retree-managed array, map, set, or object.
Do not call moveTo on a root node; roots have no parent to remove from.
Do not manually remove the node from its current parent before moving.
Move this node to a new structural parent.
The latest reproxy for this node after it moves.
This is a convenience wrapper around Retree.move. Use it from instance methods when a node should transfer ownership to another Retree-managed array, map, set, or object.
Do not call moveTo on a root node; roots have no parent to remove from.
Do not manually remove the node from its current parent before moving.
Move this node to a new structural parent.
Retree-managed destination collection or object.
The latest reproxy for this node after it moves.
This is a convenience wrapper around Retree.move. Use it from instance methods when a node should transfer ownership to another Retree-managed array, map, set, or object.
Do not call moveTo on a root node; roots have no parent to remove from.
Do not manually remove the node from its current parent before moving.
Move this node to a new structural parent.
Retree-managed destination collection or object.
Optional array insertion index, map key, or object property key.
The latest reproxy for this node after it moves.
This is a convenience wrapper around Retree.move. Use it from instance methods when a node should transfer ownership to another Retree-managed array, map, set, or object.
Do not call moveTo on a root node; roots have no parent to remove from.
Do not manually remove the node from its current parent before moving.
ProtectedmutationCreate a typed mutation function bound to this node's Convex client.
Convex mutation function reference.
A typed mutation function with optional optimistic update support.
The returned function runs the Convex mutation. It does not update Retree
state by itself. Pass withOptimisticUpdate when the mutation should
immediately update a ConvexQueryNode; otherwise wait for the
subscribed query to emit a server value.
ProtectedonRuns after this ReactiveNode receives a fresh reproxy.
Override this when a node needs to synchronize derived state only after a
real Retree change. Retree runs this before nodeChanged /
treeChanged listeners flush. If no transaction is already active,
Retree starts one so state updates made here are batched with the reproxy
that triggered the effect.
Use this for small synchronization writes that should happen only after Retree has confirmed a real change. Avoid writing unconditionally here; guard against loops by checking whether the derived value actually changed.
ProtectedonRuns when this ReactiveNode gets its first active
nodeChanged or treeChanged observer.
Override this for work that requires the proxied instance, such as starting external subscriptions that write back into Retree state.
Keep setup idempotent. Retree calls this when the first active
nodeChanged or treeChanged listener starts observing the node, not
when the node is constructed.
ProtectedonRuns when this ReactiveNode loses its last active
nodeChanged or treeChanged observer.
Use this to clean up resources created in ReactiveNode.onObserved. Do not rely on it as a destructor for unobserved nodes; it only runs after observation had started.
Prepare lazy Retree child proxies below this ReactiveNode.
Optionaloptions: IRetreePrepareTreeOptionsOptional depth limit. Omit to prepare all reachable non-ignored child objects.
Retree lazily proxies plain object and array fields on ReactiveNodes. Call
this when an app wants to pay that first-touch cost during a controlled
phase, such as while showing a loading spinner. This walks only own data
properties, so computed getters like dependencies are not evaluated or
cached as child nodes. Fields marked with @ignore are skipped.
Do not call this for every render. Call it once during setup, loading, or before a known interaction that will traverse a large subtree.
ProtectedqueryRun a Convex query once without creating a subscription.
Convex query function reference.
Query arguments. Optional for no-args queries.
Promise for the Convex query result.
Use this for imperative reads. It does not keep data live and does not emit Retree changes unless you assign the returned value into Retree state. Use ConvexNode.query when the value should stay subscribed.
Update the query arguments and resubscribe when the shallow argument
comparison changes. Pass "skip" to disable the subscription.
Next query arguments, or "skip".
Reactive paginated query node that subscribes to a Convex paginated query and exposes the loaded pages as Retree state.
Remarks
Use this directly or through ConvexNode.paginatedQuery for live paginated lists. New pages update
state,result, anderror, which can emit Retree changes and re-render React subscribers.Dispose the node when its owner is torn down. Use
"skip"when the query should be temporarily disabled.Example