Root

At the top level you create a reactive program. Call the Reactive.Root function with your reactive program callback.

const root = Reactive.Root(() => {
  // your reactive program will go here
});
Run a program and return its root

Root Object

The architecture is a dependency graph of nodes. A running program is data, and that data is grouped by scope. A scope is a block of nodes built together. The root holds the root scope the program builds, the first one created.

{
  rootScope: undefined,   // the root scope, a block of nodes, once the program has run
};
A running program held as data, from its root scope

The root does not hold nodes loose. Each node is an entry inside a scope, and every other scope is reached from the root scope through the graph. Later pages add more fields to the root as they need them: an id counter for reverse lenses, a set of drivers, and a handle to the transactions host.

The Root Scope

A scope is where nodes live. It holds the list of nodes built into it, in build order, and the node it hangs off, its parent node. The root scope has no parent.

{
  nodes:      [],          // the nodes built into this scope, in build order
  parentNode: undefined,   // the node this scope hangs off, if any
};
A scope: a block of nodes, and the node it hangs off

_scopeCreate opens one. It makes a fresh scope, remembers its parent node, and points the current scope at it. Nodes built by the callback then land inside. It runs the callback, then restores the scope that was current before. It returns the new scope object.

Reactive._scopeCreate = (parentNode, callback) => {
  const scope = { nodes: [], parentNode };
  const previousScope = Reactive._currentScope;
  Reactive._currentScope = scope;
  try {
    callback();
  } finally {
    Reactive._currentScope = previousScope;
  }
  return scope;
};
Open a scope, build into it, then return the scope

Two ambient values carry the build context, so a call site names neither the root nor the scope it builds into:

Reactive._currentRoot = undefined;
Reactive._currentScope = undefined;
The current root and the current scope

Reactive.Root

To run a program, make a fresh root, point _currentRoot at it, then open the root scope with _scopeCreate and store it on the root. That scope has no parent node, and the program's calls populate it. When the program returns, Reactive.Root restores the previous root and returns the finished root.

Reactive.Root = (programCallback) => {
  const previousRoot = Reactive._currentRoot;
  const root = {
    rootScope: undefined,
  };
  Reactive._currentRoot = root;
  try {
    root.rootScope = Reactive._scopeCreate(undefined, programCallback);
    return root;
  } finally {
    Reactive._currentRoot = previousRoot;
  }
};
Run a program and return its root

Running It

With nothing to build yet, a program is an empty function. The root it returns holds one empty scope:

const root = Reactive.Root(() => {});

root.rootScope;         // { nodes: [], parentNode: undefined }   the root scope
root.rootScope.nodes;   // []   nothing built yet
The framework runs. It has nothing to hold yet

Next Steps

A program is now a root that holds one empty scope. Next, Values adds the first nodes, Constant and State, each an entry inside that scope.