3.2 KiB
FRP tools
Observer and computed value stores designed to work together for storing real state and derived state.
observable
observable is a simple value store that can report when its value changes. It is good for wrapping
external props passed into a component so compute types can dependent on them.
Usage
Creation
Creates and sets initial value to true
const inViewport = observable(true);
Read
Call it to receive the stored value.
if (inViewport()) {
/* inViewport is truthy */
}
Change
Call it passing the new value. If any computed stores depend on this value they will be marked dirty and re-evaluated the next time they are read from.
inViewport(false);
Subscribe to changes
Call the subscribe method with a callback that will be called when the observable is changed to a
different value. The returned function can be called to unsubscribe from the observable.
const unsubscribe = inViewport.subscribe(console.log.bind(console));
computed
computed is a functional store that depends on the values of observables or other computeds. They
derive value from observables rather than store value and hence cannot be set directly.
Behavior
A computed will subscribe to its dependencies in such a way that it will be marked as dirty when
any dependency changes. Whenever it is read from, if will recompute its result if the dirty flag
is set, otherwise it just return the stored result from the last time it computed.
Usage
Creation
const showDialog = computed(
(inVP, shouldShow) => inVP && shouldShow, // computation function
[inViewport, shouldShow] // array of dependencies, can be either observable or computed
);
Read
if (showDialog()) {
/* showDialog() is truthy */
}
Call it to receive the stored value, recomputing if necessary.
Subscribe to changes
Call the subscribe method with a callback that will be called when the computed result changes to a different value. The returned function can be called to unsubscribe from the observable.
const unsubscribe = inViewport.subscribe(console.log.bind(console));
NOTE: Subscribing to a computed forces it to recompute every time an upstream dependency changes. This could negatively performance if it depends on multiple values that change sequentially and the computation function is non-trivial. For example:
const inViewport = observable(false);
const shouldShow = observable(false);
const showDialog = computed((inVP, shouldShow) => inVP && shouldShow, [inViewport, shouldShow]);
inViewport(true); // showDialog marked as dirty but does not recompute its stored result.
shouldShow(true); // showDialog is already marked as dirty. Nothing else happens.
showDialog(); // showDialog recomputes its stored result and unsets the dirty flag.
// adding a subscription will change showDialog's internal behavior
showDialog.subscribe(console.log.bind(console));
inViewport(false); // showDialog result recomputed and `false` is written to the console.
shouldShow(false); // showDialog result recomputed, console.log is not called.
showDialog(); // showDialog does not recompute, console.log is not called. `false` is returned.