Scopes
Scopes contain a block of nodes which are fixed in place. You can add and remove whole scopes in one step for the
dynamic parts of the program, for example for conditional logic (Reactive.If) or array iteration
(Reactive.Each). The root scope is the first. Scopes nest inside
one another and form a tree.
The Scope Tree
For scopes to nest, each one records the node that owns it: a parentNode, the node object whose block
this scope is. An If owns its body scope. That is what the next
step builds. The parentNode from any scope climbs the tree towards the root.
{
nodes: [], // the nodes built into this scope
parentNode, // the node object that owns this scope
};
The root scope has no owning node. Its parentNode is undefined. A climb up the tree
stops the moment it reaches a parent that is not there. As a result, that undefined parent is where
every climb ends.
_scopeCreate
A new scope now takes the owning node and stores it on the new scope. There are no ids and no map: the scope is
just an object, and _scopeCreate returns it directly. The ambient _currentScope is the
scope object itself:
Reactive._scopeCreate = (parentNode, callback) => {
const scope = { nodes: [], parentNode };
const previousScope = Reactive._currentScope;
Reactive._currentScope = scope;
try {
callback();
} finally {
Reactive._currentScope = previousScope;
}
return scope;
};
Reactive.Root
You open the root scope like any other, so Reactive.Root gives it an undefined parent.
It has no owning node, which is what makes the climb stop there. The scope object it opens is kept as
root.rootScope:
Reactive.Root = (programCallback) => {
const previousRoot = Reactive._currentRoot;
const root = {};
Reactive._currentRoot = root;
try {
root.rootScope = Reactive._scopeCreate(undefined, programCallback);
return root;
} finally {
Reactive._currentRoot = previousRoot;
}
};
Running It
The program still builds into the root scope. But that scope now records its parent, the undefined
owner that roots the tree:
const root = Reactive.Root(() => {
const one = Reactive.Constant(1);
const two = Reactive.State(2);
const sum = Reactive.Calculate([one, two], (a, b) => a + b);
});
const rootScope = root.rootScope;
rootScope.parentNode; // undefined no owning node; the climb stops here
Next Steps
With the scope tree in place, you can treat a section of the program as a unit and hang it off the node that owns it. Next, If builds a nested scope, owned by the If node, only when a condition holds.