reactive-function
A library for managing reactive data flows.
This library provides the ability to define reactive data flows by modeling application state as a directed graph and using topological sorting to compute the order in which changes should be propagated.
This library is built on top of reactive-property and graph-data-structure.
Table of Contents
Examples
Full Name
Suppose you have two reactive properties to represent someone's first and last name.
var firstName = ;var lastName = ;
Another reactive property can represent the full name of the person.
var fullName = ;
You could set the full name value like this.
;
However, the above code sets the value of fullName
only once. Here's how you can define a ReactiveFunction that automatically updates fullName
whenever firstName
or lastName
change.
var reactiveFunction = ;
The data flow graph for the example code above.
Whenever firstName
or lastName
change, the callback defined above will be executed on the next animation frame. If you don't want to wait until the next animation frame, you can force a synchronous evaluation of the data flow graph by invoking digest.
ReactiveFunction;
Now you can access the computed fullName
value by invoking it as a getter.
console; // Prints "Jane Smith"
ABC
The output of one reactive function can be used as an input to another.
Here, b is both an output and an input.
var a = ;var b = ;var c = ; ; ; ReactiveFunction;assert;
Tricky Case
This is the case where Model.js fails because it uses Breadth-first Search to propagate changes. In this graph, propagation using breadth-first search would cause e
to be set twice, and the first time it would be set with an inconsistent state. This fundamental flaw cropped up as flashes of inconstistent states in some interactive visualizations built on Model.js. For example, it happens when you change the X column in this Magic Heat Map. This flaw in Model.js is the main inspiration for making this library and using topological sort, which is the correct algorithm for propagating data flows and avoiding inconsistent states.
The tricky case, where breadth-first propagation fails.
var a = ;var b = ;var c = ;var d = ;var e = ; ;;;; ReactiveFunction;assert; ;ReactiveFunction;assert;
Ohm's Law
Ohm's Law is a mathematical relationship between 3 quantities in electrical circuits:
- V, voltage. V = IR
- I, current. I = V/R
- R, resistance. R = V/I
Given any two of these values, one can calculate the third. Here's an example where if any two of the values are set, the third will automatically be calculated.
The data flow graph for Ohm's Law.
var I = ;var V = ;var R = ; ;;; ReactiveFunction;console; // Prints 4.5 ReactiveFunction;console; // Prints 12 ;;ReactiveFunction;console; // Prints 0.5
For more detailed example code, have a look at the tests.
Installing
If you are using NPM, install this package with:
npm install reactive-function
Require it in your code like this:
var ReactiveFunction = ;
This library is designed to work with reactive-property, you'll need that too.
npm install reactive-property
var ReactiveProperty = ;
API Reference
Managing Reactive Functions
# ReactiveFunction(options)
Construct a new reactive function. The options argument should have the following properties.
- inputs - The input properties. An array of ReactiveProperty instances.
- output (optional) - The output property. An instance of ReactiveProperty.
- callback - The reactive function callback. Arguments are values of inputs. The return value will be assigned to output.
This constructor sets up a reactive function such that callback be invoked
- when all input properties are defined,
- after any input properties change,
- during a digest.
An input property is considered "defined" if it has any value other than undefined
. The special value null
is considered to be defined.
An input property is considered "changed" when
- the reactive function is initially set up, and
- whenever its value is set.
Input properties for one reactive function may also be outputs of another.
# reactiveFunction.destroy()
Cleans up resources allocated to this reactive function.
More specifically:
- Removes listeners from inputs.
- Removes edges from the data flow graph (from each input).
- Removes property nodes from the data flow graph if they have no incoming or outgoing edges.
You should invoke this function when finished using reactive functions in order to avoid memory leaks.
# ReactiveFunction.link(propertyA, propertyB)
Sets up one-way data binding from propertyA to propertyB. Returns an instance of ReactiveFunction.
Arguments:
- propertyA - A reactive-property.
- propertyB - A reactive-property that will be set to the value of propertyA and updated whenever propertyA changes.
Example:
var a = ;var b = ;var link = ReactiveFunctionlinka b;
This is equivalent to:
var a = ;var b = ;var link = ;
Data Flow Execution
# ReactiveFunction.digest()
Propagates changes from input properties through the data flow graph defined by all reactive properties using topological sorting. An edge in the data flow graph corresponds to a case where the output of one reactive function is used as an input to another.
Whenever any input properties for any reactive function change, digest is debounced (scheduled for invocation) on the nextFrame. Because it is debounced, multiple synchronous changes to input properties collapse into a single digest invocation.
Digests are debounced to the next animation frame rather than the next tick because browsers will render the page at most every animation frame (approximately 60 frames per second). This means that if DOM manipulations are triggered by reactive functions, and input properties are changed more frequently than 60 times per second (e.g. mouse or keyboard events), the DOM manipulations will only occur at most 60 times per second, not more than that.
# ReactiveFunction.nextFrame(callback)
Schedules the given function to execute on the next animation frame or next tick.
This is a simple polyfill for requestAnimationFrame that falls back to setTimeout. The main reason for having this is for use in the tests, which run in a Node.js environment where requestAnimationFrame
is not available. Automatic digests are debounced against this function.
Serialization
# ReactiveFunction.serializeGraph()
Serializes the data flow graph. Returns an object with the following properties.
nodes
An array of objects, each with the following properties.id
The node identifier string.propertyName
The property name for this node, derived fromproperty.propertyName
for each property.
links
An array of objects representing edges, each with the following properties.source
The node identifier string of the source node (u).target
The node identifier string of the target node (v).
Example:
var firstName = ;var lastName = ;var fullName = ; // For serialization.firstNamepropertyName = "firstName";lastNamepropertyName = "lastName";fullNamepropertyName = "fullName"; ; var serialized = ReactiveFunction;
The value of serialized
will be:
See also: