DerivableJS
State made simple → Effects made easy
Derivables are an Observable-like state container with superpowers. Think MobX distilled to a potent essence, served with two heaped spoonfuls of extra performance, a garnish of side effects innovation, and a healthy side-salad of immutability.
This README is work in progress. Please refer to the master branch's README for rationale etc.
Quick start
There are two types of Derivable:
-
Atoms
Atoms are simple mutable references to immutable values. They represent the ground truth from which all else is derived.
import {atom} from 'derivable';
const $Name = atom('Richard');
$Name.get();
$Name.set('William');
$Name.get();
N.B. The dollar-sign prefix is just a convention I personally use to create a syntactic distinction between ordinary values and derivable values.
-
Derivations
Derivations represent pure (as in 'pure function') transformation of values held in atoms. You can create them with the .derive
method, which is a bit like the .map
method of Arrays and Observables.
const cyber = word => word.toUpperCase().split('').join(' ');
const $cyberName = $Name.derive(cyber);
$cyberName.get();
$Name.set('Sarah');
$cyberName.get();
Derivations cannot be modified directly with .set
, but change in accordance with their dependencies, of which there may be many. Here is an example with two dependencies which uses the fundamental derivation
constructor function:
import {derivation} from 'derivable';
const $Transformer = atom(cyber);
const $transformedName = derivation(() =>
$Transformer.get()($Name.get())
);
$transformedName.get();
const reverse = string => string.split('').reverse().join('');
$Transformer.set(reverse);
$transformedName.get();
$Name.set('Fabian');
$transformedName.get();
derivation
takes a function of zero arguments which should
dereference one or more Derivables to compute the new derived value. DerivableJS then sneakily monitors who
is dereferencing who to infer the parent-child relationships.
Reactors
Declarative state management is nice in and of itself, but the real benefits come from how it enables us to more effectively manage side effects. DerivableJS has a really nice story on this front: changes in atoms or derivations can be monitored by things called Reactors, which do not themselves have any kind of 'current value', but are more like independent agents which exist solely for executing side effects.
Let's have a look at a tiny example app which greets the user:
import {atom, derivation, transact} from 'derivable'
const $Name = atom("World");
const $CountryCode = atom("en");
const greetings = {
en: "Hello",
de: "Hallo",
es: "Hola",
cn: "您好",
fr: "Bonjour",
};
const $greeting = $CountryCode.derive(cc => greetings[cc]);
const $message = derivation(() =>
`${$greeting.get()}, ${$name.get()}!`
);
$message.react(
msg => console.log(msg),
{when: $greeting}
);
$CountryCode.set("de");
$Name.set("Dagmar");
transact(() => {
$CountryCode.set("fr");
$Name.set("Étienne");
});
$CountryCode.set('dk');
The structure of this example can be depicted as the following DAG:
Usage
DerivableJS is becoming fairly mature, and has been used for serious stuff in production with very few issues. I think it is safe to consider it beta quality at this point.
If your app is non-trivial, use Immutable.
With React
The fantastic project react-derivable lets you use
derivables in your render method, providing seamless interop with component-local state and props.
Debugging
Due to inversion of control, the stack traces you get when your derivations throw errors can be totally unhelpful. There is a nice way to solve this problem for dev time. See setDebugMode for more info.
Examples
Coming soon.
Browser
Either with browserify/webpack/common-js-bundler-du-jour, or clone the repo, run npm install && npm run build
, then grab the UMD bundle from dist/derivable.umd[.min].js
(source maps are also available).
import { withEquality } from 'derivable'
const { atom, derive, ..._} = withEquality(myCustomEqualityChecker);
Contributing
I heartily welcome questions, feature requests, bug reports, and general suggestions/criticism on the github issue tracker. I also welcome bugfixes via pull request (please read CONTRIBUTING.md before sumbmitting).
Inspiration <3
License
Copyright 2015 David Sheldrick <djsheldrick@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.