StaticclearClear all listeners for a given node.
node to clear all listeners for
when false, will unsubscribe to all child nodes as well.
StaticcloneClone a Retree-managed node into a detached object that can be assigned somewhere else as a new structural child.
Existing Retree-managed node to copy.
A detached copy of the node's current raw data.
Use this when two places need independent state initialized from the same current data. The clone is detached until you assign it into a Retree tree. Mutating the clone after assignment emits for the clone's new structural location, not the source node.
Do not use clone when the original object should simply move; use
Retree.move. Do not use clone for a selected-item pointer; use
Retree.link or @link.
StaticlinkCreate a reactive pointer to an existing Retree-managed node.
Existing Retree-managed node to point at.
A Retree-managed link object whose current points at node.
The returned link can be stored in a Retree tree without reparenting the
linked target. Replacing link.current emits for the link; mutating the
linked target emits from its structural location.
Use this for selected items, cross-references, and pointers into another part of the same tree. Do not use a link when ownership should move; use Retree.move instead. Do not use a link when the two locations should diverge independently; use Retree.clone instead.
const root = Retree.root({
tasks: [{ title: "Docs" }],
selected: null as RetreeLink<{ title: string }> | null,
});
root.selected = Retree.link(root.tasks[0]); // ✅ emits on root
root.selected.current.title = "Better docs"; // ✅ emits where task is owned
Retree.parent(root.selected.current) === root.tasks; // true
StaticmoveMove an existing Retree-managed node from its current parent to a new parent.
The latest reproxy for the moved node.
Retree is a pure tree: each node has one structural parent. Use move
when ownership should transfer from the old parent to the destination.
Retree finds the current parent with Retree.parent and removes
the node safely before inserting it into the destination.
Arrays accept an optional numeric insertion index. Maps and plain
objects require a key. Sets ignore the key. Do not manually delete the
node from its old parent before calling move.
Move an existing Retree-managed node from its current parent to a new parent.
The latest reproxy for the moved node.
Retree is a pure tree: each node has one structural parent. Use move
when ownership should transfer from the old parent to the destination.
Retree finds the current parent with Retree.parent and removes
the node safely before inserting it into the destination.
Arrays accept an optional numeric insertion index. Maps and plain
objects require a key. Sets ignore the key. Do not manually delete the
node from its old parent before calling move.
Move an existing Retree-managed node from its current parent to a new parent.
The latest reproxy for the moved node.
Retree is a pure tree: each node has one structural parent. Use move
when ownership should transfer from the old parent to the destination.
Retree finds the current parent with Retree.parent and removes
the node safely before inserting it into the destination.
Arrays accept an optional numeric insertion index. Maps and plain
objects require a key. Sets ignore the key. Do not manually delete the
node from its old parent before calling move.
Move an existing Retree-managed node from its current parent to a new parent.
Existing Retree-managed node to move.
Retree-managed array, map, set, or object destination.
Insertion index for arrays, map key for maps, or property key for objects.
The latest reproxy for the moved node.
Retree is a pure tree: each node has one structural parent. Use move
when ownership should transfer from the old parent to the destination.
Retree finds the current parent with Retree.parent and removes
the node safely before inserting it into the destination.
Arrays accept an optional numeric insertion index. Maps and plain
objects require a key. Sets ignore the key. Do not manually delete the
node from its old parent before calling move.
StaticonListen for changes to a node.
the object to listen for changes to.
the type of TRetreeEvents change events to listen to.
the callback function for your listener.
an unsubscribe function to clean up your listeners.
Use nodeChanged for changes directly owned by the node.
Use treeChanged for changes to the node or descendants.
Use nodeRemoved for when this node is removed from its parent.
// Create the root node
const counter = Retree.root({ count: 0 });
// Listen for changes to values of the node
const unsubscribe = Retree.on(counter, "nodeChanged", (reproxy) => {
console.log(reproxy !== counter); // output: false
console.log(reproxy.count === counter.count); // output: true
});
// Make a change
counter.count = counter.count + 1;
// Stop listening for changes
unsubscribe();
StaticparentGet a parent node for a given child node, if it exists
a child node to get the parent of
the parent node if it exists, otherwise null
const tree = Retree.root({
count: 0,
child: {
count: 0,
child: {
count: 0,
},
},
});
function recursiveLog(node) {
console.log(node.count);
// Get the parent of node, if it exists
const parent = Retree.parent(node);
if (!parent) return; // at top of tree
recursiveLog(parent);
}
Retree.on(tree.child.child, "nodeChanged", (child) => {
// Recursively log the count of this node and all its parents
recursiveLog(child);
});
tree.child.child.count = 1;
StaticrootBuilds a Retree compatible root node for the root object of your tree.
a root TreeNode for your tree
a Retree compatible object of type T
Use this once where plain state enters Retree. The returned proxy is
compatible with Retree.on, Retree.parent,
Retree.move, Retree.link, and React hooks from
@retreejs/react.
Do mutate the returned tree directly with normal JavaScript assignment
and collection methods. Do not call Retree.root(...) on every child
you assign into the tree; Retree prepares children as they are attached
or read.
StaticrunRun a synchronous transaction that will not cause `Retree.on listeners to emit.
transaction function to run
skip reproxying nodes such that subsequent comparisons are equal. defaults to true.
Use runSilent for non-rendered bookkeeping or integration state that
should update without notifying Retree listeners. By default it also
skips reproxying, so old and new object identities stay equal for later
comparison checks.
Pass skipReproxy = false when you want to suppress listener emission
but still refresh reproxy identities.
StaticrunRun a synchronous transaction that will not cause Retree.on listeners for changed nodes to emit multiple times.
transaction function to run
If multiple nodes changed during the transaction, Retree.on events will be emitted for each node that changed.
If using React, this should still be flattened to a single render, but it is not guaranteed.
It may be reasonable to combine this with React.startTransition if this is a concern.
StaticselectSubscribe to a derived value from any Retree-managed node.
Retree-managed node to observe.
Function that reads a selected value or dependency list from the latest reproxy.
Called only when the selected value or dependency list changes.
Optionaloptions: RetreeSelectOptions<TSelected>Optional listener type and equality comparison for the whole selected value or tuple.
Unsubscribe function.
select recomputes the selected value when the observed node or selected
reactive dependencies emit, then calls callback only when the selection
changes. Selectors may return one value or an ordered dependency list.
Reactive entries in a dependency list are subscribed to; primitive and
plain entries are compared by identity.
Dependency-list subscriptions are observational: if a selected dependency
emits, select calls your callback when the selection changes, but it
does not force the node passed to select to receive a fresh reproxy.
Use @select when a ReactiveNode owner should emit nodeChanged.
This is a subscription primitive, not a memo cache: use memo /
fnMemo to cache computation, and select to narrow notifications.
By default select listens to nodeChanged, which is correct when the
selector reads fields directly owned by node. Pass
listenerType: "treeChanged" when the selector intentionally reads
descendants that are not included as reactive entries in a dependency
list.
You can also call Retree.select(() => value, callback) without a node.
That form traps reads automatically. Whole Retree-managed values are
subscribed to broadly. Property reads subscribe to the owner node but
compare the specific property value, so task.done can react to task
replacement or done changes without reacting to unrelated task fields.
Primitive reads are kept as comparison values, so the callback only runs
when the trapped reads make the selected value or dependency set change.
const project = Retree.root({
tasks: [{ done: false }, { done: true }],
});
const unsubscribe = Retree.select(
project.tasks,
(tasks) => tasks.filter((task) => task.done).length,
(next, previous) => console.log({ next, previous }),
{ listenerType: "treeChanged" }
);
project.tasks[0].done = true; // ✅ callback: 1 -> 2
project.tasks[0].done = true; // ❌ selected value did not change
unsubscribe();
Subscribe to a derived value from any Retree-managed node.
Function that reads a selected value or dependency list from the latest reproxy.
Called only when the selected value or dependency list changes.
Optionaloptions: RetreeSelectOptions<ReturnType<TSelector>>Optional listener type and equality comparison for the whole selected value or tuple.
Unsubscribe function.
select recomputes the selected value when the observed node or selected
reactive dependencies emit, then calls callback only when the selection
changes. Selectors may return one value or an ordered dependency list.
Reactive entries in a dependency list are subscribed to; primitive and
plain entries are compared by identity.
Dependency-list subscriptions are observational: if a selected dependency
emits, select calls your callback when the selection changes, but it
does not force the node passed to select to receive a fresh reproxy.
Use @select when a ReactiveNode owner should emit nodeChanged.
This is a subscription primitive, not a memo cache: use memo /
fnMemo to cache computation, and select to narrow notifications.
By default select listens to nodeChanged, which is correct when the
selector reads fields directly owned by node. Pass
listenerType: "treeChanged" when the selector intentionally reads
descendants that are not included as reactive entries in a dependency
list.
You can also call Retree.select(() => value, callback) without a node.
That form traps reads automatically. Whole Retree-managed values are
subscribed to broadly. Property reads subscribe to the owner node but
compare the specific property value, so task.done can react to task
replacement or done changes without reacting to unrelated task fields.
Primitive reads are kept as comparison values, so the callback only runs
when the trapped reads make the selected value or dependency set change.
const project = Retree.root({
tasks: [{ done: false }, { done: true }],
});
const unsubscribe = Retree.select(
project.tasks,
(tasks) => tasks.filter((task) => task.done).length,
(next, previous) => console.log({ next, previous }),
{ listenerType: "treeChanged" }
);
project.tasks[0].done = true; // ✅ callback: 1 -> 2
project.tasks[0].done = true; // ❌ selected value did not change
unsubscribe();
Staticuse
Main entry point for use with Retree package. Exposes utility functions for observing to changes to an object and its children.