anod is a reactive library based on S.js and SArray, but with several changes inspired by Solid, ivi and Signals by PreactJS. It provides lightweight wrapper objects that turn regular values and arrays into reactive objects.
import { value, batch, effect } from "anod";
import { array } from "anod/array";
function Member(age, name) {
this.age = age;
this.name = name;
}
const members = array([
new Member(21, "Leif"),
new Member(35, "Siv"),
new Member(48, "Sonja")
]);
const eventPlanned = value(false);
const childExists = members.some(member => member.age < 18);
const memberNames = members.map(member => member.name).join(", ");
effect(() => {
const childText = childExists.val() ? " (has children)" : "";
const eventText = eventPlanned.val() ? " Event planned, stay tuned!" : "";
console.log(`Member list${childText}: ${memberNames.val()}.${eventText}`);
});
// Prints "Member list: Leif, Siv, Sonja."
members.push(new Member(15, "Lars"));
// Prints "Member list (has children): Leif, Siv, Sonja, Lars."
members.unshift(new Member(28, "Astrid"));
// Prints "Member list (has children): Astrid, Leif, Siv, Sonja, Lars."
batch(function() {
members.pop();
eventPlanned.set(true);
});
// Prints "Member list: Astrid, Leif, Siv, Sonja. Event planned, stay tuned!"
Creates a root wrapper allowing to dispose any children computations created.
import { root, value, effec } from "anod";
const r1 = root(function() {
const v1 = value(1);
effect(function() {
console.log(v1.val());
});
});
v1.set(2); // prints 2
r1.dispose();
v1.set(3); // nothing happens, effect is disposed
The basic data types are data
and value
. data
changes whenever set is called, whereas value
only updates when set to a different value.
import { value, data, effect } from "anod";
const v1 = value(true);
const d1 = data(true);
effect(function() {
if (v1.val()) {
console.log("v1");
}
if (d1.val()) {
console.log("d1");
}
});
v1.set(true); // nothing happens
v1.set(false); // runs effect
d1.set(true); // runs effect
Both data
and value
expose peek
, which allows reading the value without registering a dependency when called inside a computation or effect.
compute
allows for creating derived signals. The main purpose is to avoid unnecessary re-calculations.
import { value, compute, effect } from "anod";
const $age = value(0);
const $stage = compute(function() {
const age = $age.val();
return age < 10 ? "child" : age < 18 ? "teenager" : "adult";
});
effect(function() {
console.log($stage.val());
});
$age.set(5); // will not trigger effect
$age.set(14); // will trigger, value changed from child to teenager
compute
is lazy, and only runs when any other compute or effect tracks its value.
effect
is for creating any functionality derived from signals. It runs upon creation, and whenever any of its dependencies trigger a change.
batch
applies all changes before updating computes and effects.
sample
works like peek
, but takes a callback, and does not register any dependency within it.
Registers a cleanup function that runs whenever a compute/effect updates. When final parameter is true, the caller is being disposed.
array
creates a reactive array object that shims native functions. Mutating functions (push, pop, shift etc) behave like calling set for data
. It also exposes several derivations of compute
, for non-mutating methods.
ComputeReduce
and ComputeArray
both derive from the core Compute
class. ComputeReduce
exposes a compute
value for methods like reduce, every, some etc. ComputeArray
inherits from the abstract base class ReactiveIterator
that is shared with array
. This is for methods that return an array, such as map and filter.
import { effect } from "anod";
import { array } from "anod/array";
const a1 = array([1,2,3]);
const sum = a1.filter(v => v < 3).map(v => v * 2).reduce((acc, val) => acc + val);
effect(function() {
console.log(sum.val()); // prints 6
});
a1.unshift(1, 2, 3); // prints 12
a1.push(4, 5, 6); // does not trigger, value has not changed