Sprites.Reactive

The reactive core. Values hold state, calculations read and derive, and the structure grows and shrinks with the data. Context injects a value for a subtree, read by any descendant.

Async work lives in drivers, outside the synchronous core. A value can be pending, with no value yet; reading a pending value under a Sprites.Ui.Loading shows a spinner. Sprites.Animate holds the time based drivers.

Sprites.Reactive

Sprites.Reactive.Context

Sprites.Reactive.Calculate Sprites.Reactive.Value

Sprites.Reactive.Calculate(fn)

Read values and return a value. Going forward the result is a read-only reactive value, recomputed when a value it read changes. Inside a lens it builds the same live value, which a Set commits when it is ready, or when it lands if an async part is still on the way, so a calculation function reads the same in both directions.

InputTypeDescription
fnFunctionReads values with Get and plain work, returning the computed result.

Returns a read-only Sprites.Reactive.Value holding the result, recomputed when a value it read changes.

const w = Sprites.Reactive.Value(4);
const h = Sprites.Reactive.Value(3);
const area = Sprites.Reactive.Calculate(() => {
  return Sprites.Reactive.Get(w) * Sprites.Reactive.Get(h);
});
Sprites.Ui.Input.Number(w);
Sprites.Ui.Input.Number(h);
Sprites.Ui.Dom.Text('Area: ', area);
Change either number and the area recomputes.

Sprites.Reactive.Driver Object

Sprites.Reactive.Driver(name, resultBinding, request, make)

Ask a driver for async work. The driver runs the work and writes the result value, which opens the next update. The first request for a name builds the driver with make; the request lives until its scope ends. Drivers keep the core synchronous, so no calculation or handler waits.

The request may be a reactive value, so the driver follows it: when the request changes the driver reruns with the new value, cancelling any stale work, and writes the same result value. A read whose query is a reactive object stays live this way, with no rebuild. A plain request dispatches once.

A request emitted inside a handler freezes the values that handler read until the driver reports, and marks the result pending. A control that writes a frozen value disables on its own, and a control that reads a pending value shows a spinner under a Loading, so the interface locks the right parts while the work runs.

InputTypeDescription
nameStringThe driver's name. The same name ties every call to one driver.
resultBindingSprites.Reactive.ValueThe value the driver writes as the work runs and completes.
requestAny or Sprites.Reactive.ValueThis request's parameters. A reactive value reruns the driver whenever it changes.
makeFunctionBuilds the driver the first time the name is seen, returning onRequestAdd and onDriverDestroy.

Returns the request control, with update and destroy.

const status = Sprites.Reactive.Pending('');
const go = Sprites.Reactive.Value(false);
const delay = () => {
  return {
    onRequestAdd: (binding, params) => {
      const id = setTimeout(() => {
        Sprites.Reactive.batch(() => {
          Sprites.Reactive.Set(binding, params.text);
        });
      }, params.ms);
      return { onRequestDestroy: () => { clearTimeout(id); } };
    },
  };
};
Sprites.Ui.Button.SetValue(go, true, 'Start');
Sprites.Ui.Loading(() => {
  Sprites.Ui.Dom.Text(' status = ', status);
});
Sprites.Reactive.If(go, () => {
  Sprites.Reactive.Driver('demo.delay', status, { text: 'ready', ms: 1500 }, delay);
});
Press Start; the driver writes the value after a delay, and the spinner clears.

Sprites.Reactive.Each Void

Sprites.Reactive.Each(list, build)

Repeat the content for each element of a list.

InputTypeDescription
listArray or Sprites.Reactive.ValueThe list to walk. A reactive list rebuilds as it changes, and each item is a two-way field.
buildFunctionCalled as build(item, index) to build the content for each element.

Returns nothing.

const items = Sprites.Reactive.Value(['Red', 'Green']);
Sprites.Ui.Button.Push(items, () => {
  return 'Blue';
}, 'Add colour');
Sprites.Reactive.Each(items, (item) => {
  Sprites.Ui.Dom.Tag('p', item);
});
Add a colour and a new paragraph appears.

Sprites.Reactive.Field Sprites.Reactive.Value

Sprites.Reactive.Field(source, key)

A two-way view of one field or element. Reading tracks the container; setting writes the whole container back, up any lens chain. Inside a lens or a reverse handler it fetches the field and returns it, with no binding; to write a field there, Set the whole container.

InputTypeDescription
sourceSprites.Reactive.ValueThe container value, an object or an array.
keyString or NumberThe field name or array index to view.

Returns a two-way Sprites.Reactive.Value over that field; setting it writes a fresh container back.

const point = Sprites.Reactive.Value({ x: 1, y: 2 });
const x = Sprites.Reactive.Field(point, 'x');
Sprites.Ui.Input.Number(x);
Sprites.Ui.Dom.Text('x = ', x);
Editing the field writes a fresh point object back.

Sprites.Reactive.Freeze Any

Sprites.Reactive.Freeze(value)

Read a snapshot of a value without following it. Unlike Get, it never subscribes, so the reader does not update when the value later changes; it hands back the value now. Use it in a lens, or in reactive value code outside a Calculate, where you want the value as it stands. Use Get inside a Calculate to derive a value that follows. In a reverse handler Freeze records the read, so the transaction can freeze what it depends on.

InputTypeDescription
valueAny or Sprites.Reactive.ValueA constant, returned as is, or a reactive value to snapshot.

Returns the value now.

const step = Sprites.Reactive.Value(5);
const total = Sprites.Reactive.Value(0);
const add = Sprites.Reactive.Value(null, () => {
  const s = Sprites.Reactive.Freeze(step);
  const t = Sprites.Reactive.Freeze(total);
  Sprites.Reactive.Set(total, t + s);
});
Sprites.Ui.Input.Number(step, 1, 100);
Sprites.Ui.Button.Act(add, 'Add step');
Sprites.Ui.Dom.Text('total = ', total);
The lens freezes the step and adds it, so the total climbs by the step as it stands on each press.

Sprites.Reactive.Get Any

Sprites.Reactive.Get(value)

Read a value. A constant is returned as is. A reactive value gives its current value; in a calculate or binding it subscribes, and elsewhere it is a snapshot, so reading in build code captures the value once.

InputTypeDescription
valueAny or Sprites.Reactive.ValueA constant, returned as is, or a reactive value to read.

Returns the current value.

const price = Sprites.Reactive.Value(5);
const label = Sprites.Reactive.Calculate(() => {
  return 'Price: ' + Sprites.Reactive.Get(price);
});
Sprites.Ui.Input.Number(price);
Sprites.Ui.Dom.Text(label);
Get reads the price inside a calculation, so the label follows it.

Sprites.Reactive.If Void

Sprites.Reactive.If(cond, build)

Show the content while a value is true.

InputTypeDescription
condBoolean, Sprites.Reactive.Value or FunctionThe condition. A reactive condition shows and hides the content as it changes.
buildFunctionBuilds the content shown while the condition is true.

Returns nothing.

const open = Sprites.Reactive.Value(true);
Sprites.Ui.Input.Checkbox(open);
Sprites.Reactive.If(open, () => {
  Sprites.Ui.Dom.Tag('p', 'Now you see me');
});
Uncheck the box to hide the paragraph.

Sprites.Reactive.IfNot Void

Sprites.Reactive.IfNot(cond, build)

The mirror of If: show the content while a value is false, so a part shown only while something is absent needs no separate negation.

InputTypeDescription
condBoolean, Sprites.Reactive.Value or FunctionThe condition. The content shows while it is false.
buildFunctionBuilds the content shown while the condition is false.

Returns nothing.

const full = Sprites.Reactive.Value(false);
Sprites.Ui.Input.Checkbox(full);
Sprites.Reactive.IfNot(full, () => {
  Sprites.Ui.Dom.Tag('p', 'There is still room.');
});
Check the box and the message hides.

Sprites.Reactive.Pending Sprites.Reactive.Value

Sprites.Reactive.Pending(initial)

A value that is loading. It holds a realistic stand-in of the right shape, an empty array, a blank string, zero or false, so a reader can use it while it waits. It is pending, so reading it under a Sprites.Ui.Loading shows the spinner and a calculation over it stays pending too. The first Set gives it the real value and clears the flag. It stays writable, so a driver may fill it.

InputTypeDescription
initialAnyThe stand-in value of the right shape, used while the value loads.

Returns a pending Sprites.Reactive.Value, unreadable until set.

const result = Sprites.Reactive.Pending('');
const started = Sprites.Reactive.Value(false);
Sprites.Ui.Button.SetValue(started, true, 'Load');
Sprites.Ui.Loading(() => {
  Sprites.Ui.Dom.Text(' result = ', result);
});
Sprites.Reactive.If(started, () => {
  Sprites.Animate.SetAfter(result, 'done', 1500);
});
The value shows a spinner until SetAfter fills it.

Sprites.Reactive.Repeat Void

Sprites.Reactive.Repeat(count, build)

Repeat the content count times, calling build with each index. count may be a constant or a reactive value, so the run grows and shrinks as it changes.

InputTypeDescription
countNumber, Sprites.Reactive.Value or FunctionHow many times to repeat. A reactive count grows and shrinks the run.
buildFunctionCalled as build(index) for each repetition.

Returns nothing.

const count = Sprites.Reactive.Value(3);
Sprites.Ui.Input.Number(count, 0, 6);
Sprites.Reactive.Repeat(count, (i) => {
  Sprites.Ui.Dom.Tag('button', 'Item ' + (i + 1));
});
Change the count and the run of buttons follows.

Sprites.Reactive.Set Any

Sprites.Reactive.Set(value, next)

Write a value, in an event handler or a lens. A lens set runs its onChange. Takes a reactive value or a constant, never a function; it throws on a function. Give it a live value, such as a calculation, and it commits when that value is ready, or when it lands if an async part is still on the way.

InputTypeDescription
valueSprites.Reactive.ValueThe value to write. Allowed only in a handler or a lens.
nextAny or Sprites.Reactive.ValueThe new value: a reactive value or a constant, never a function.

Returns the value written.

const n = Sprites.Reactive.Value(0);
Sprites.Ui.Button.Increment(n, 'Add one');
Sprites.Ui.Dom.Text('n = ', n);
Each click sets n to one more than before.

Sprites.Reactive.Value Sprites.Reactive.Value

Sprites.Reactive.Value(value, onChange)

A reactive value. The first argument is a constant or a reactive value; a reactive one makes it a binding that tracks that value. With onChange it is a lens, or a two-way binding when tracking, and setting it runs onChange to write other values. Made inside a lens or a reverse handler it is a nested reverse process: it holds no path or probe, and setting it runs its onChange in place, in the same reverse sweep, joining the transaction. A binding over a reactive source returns the source itself in that direction, since it has no tracking to do there.

InputTypeDescription
valueAny or Sprites.Reactive.ValueA constant for state, or a reactive value to track as a binding.
onChangeFunctionOptional. Makes it a lens, run on set to write other values.

Returns a reactive Sprites.Reactive.Value.

const count = Sprites.Reactive.Value(2);
Sprites.Ui.Input.Number(count);
Sprites.Ui.Dom.Text('Count: ', count);
The text follows the value as you change it.

Sprites.Reactive.Context.Get Any

Sprites.Reactive.Context.Get(name, def)

Read the nearest value in scope for a name, or a default when no Set is in scope.

InputTypeDescription
nameStringThe context name to read.
defAnyOptional value returned when the name is not in scope.

Returns the nearest value in scope for the name, or the default.

const theme = Sprites.Reactive.Value('dark');
Sprites.Reactive.Context.Set('theme', theme, () => {
  const t = Sprites.Reactive.Context.Get('theme', 'light');
  Sprites.Ui.Input.Text(t);
  Sprites.Ui.Dom.Text('theme = ', t);
});
Read the nearest value in scope, or a default.

Sprites.Reactive.Context.Set Any

Sprites.Reactive.Context.Set(name, value, build)

Inject a named value for the subtree that build creates, then restore. A nearer Set shadows a wider one of the same name.

InputTypeDescription
nameStringThe context name.
valueAnyThe value to inject for the subtree.
buildFunctionBuilds the subtree that can read the value.

Returns the result of build.

const theme = Sprites.Reactive.Value('dark');
Sprites.Reactive.Context.Set('theme', theme, () => {
  const t = Sprites.Reactive.Context.Get('theme');
  Sprites.Ui.Input.Text(t);
  Sprites.Ui.Dom.Text('theme = ', t);
});
Inject a value for the subtree; a descendant reads and edits it.