Composing computations
A state closure brings together inputs, a computation, and the lifetime of its children. Stative offers three declaration forms. All three can be created with render(S([Declaration, inputs])) or described with JSX.
Choose a declaration form
Start with a pure function. Choose once when you need to connect a state flow, or a class when instance methods are useful. These forms can be nested together; you do not need to choose one for the whole project.
Pure function: calculate the current result
A plain function receives values that have already been resolved. This one formats a price from quantity and unit price:
The function runs when its output is first accessed, then again when its dependencies change. Keep it pure: return a result from the inputs without starting timers or opening external connections.
once function: connect a state flow
A function wrapped in once builds its state flow on the first output access. Changing inputs use IReadableClosure<T>; the static unit price remains a plain number:
When the quantity changes, the once body does not run again. The computations connected by useSelect and useMap update the result.
useMap and useSelect also accept arrays of sources, such as useMap([left, right], ([a, b]) => a + b).
Call these helpers synchronously inside the once body. They belong to Stative's runtime and are not React Hooks.
Class: organize a computation around an instance
Extend BaseStateClosure and connect the inputs to the output in render():
this.map, this.select, and this.create correspond to the helpers used inside once. A class can also expose its own instance methods. Creating it with render(S([YourClass, inputs])) preserves its concrete instance type.
Create each form the same way
Choose any one of the PriceLabel declarations above. The calling code stays the same:
The key distinction is the input: a pure function receives the current numeric quantity, while once and class declarations receive a readable closure.
Nest computations
A computation's output can become another computation's input. Place another S descriptor in an input to connect them:
label manages the nested child computations. Destroying it also releases those children.
Pass ordinary options
In once and class declarations, primitive constants such as numbers and strings pass through unchanged. Wrap static objects, arrays, and ordinary callbacks in D() to use them as plain values:
To provide a constant as a reactive input, wrap it in ReactiveState.of(value). D() preserves a value; it does not make an object reactive. Callbacks that create child closures have their own composition behavior; see render callbacks in JSX.