ds300 / derivablejs

Functional Reactive State for JavaScript and TypeScript
Apache License 2.0
515 stars 23 forks source link

DerivableJS

State made simple → Effects made easy

npm Build Status Coverage Status Join the chat at https://gitter.im/ds300/derivablejs Empowered by Futurice's open source sponsorship program .min.gz size

Derivables are an Observable-like state container with superpowers. Think MobX distilled to a potent essence, served with two heaped tablespoons of extra performance, a garnish of declarative effects management, and a healthy side-salad of immutability.

Quick start

There are two types of Derivable:

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, derive, transact } from "derivable";

// global application state
const name = atom("World"); // the name of the user
const countryCode = atom("en"); // for i18n

// static constants don't need to be wrapped
const greetings = {
  en: "Hello",
  de: "Hallo",
  es: "Hola",
  cn: "您好",
  fr: "Bonjour"
};

// derive a greeting message based on the user's name and country.
const greeting = derive(() => greetings[countryCode.get()]);
const message = derive(() => `${greeting.get()}, ${name.get()}!`);

// set up a Reactor to print the message every time it changes, as long as
// we know how to greet people in the current country.
message.react(msg => console.log(msg), { when: greeting });
// $> Hello, World!

countryCode.set("de");
// $> Hallo, World!
name.set("Dagmar");
// $> Hallo, Dagmar!

// we can avoid unwanted intermediate reactions by using transactions
transact(() => {
  countryCode.set("fr");
  name.set("Étienne");
});
// $> Bonjour, Étienne!

// if we set the country code to a country whose greeting we don't know,
// `greeting` becomes undefined, so the `message` reactor won't run
// In fact, the value of `message` won't even be recomputed.
countryCode.set("dk");
// ... crickets chirping

The structure of this example can be depicted as the following DAG:

Key differences with MobX

API / Documentation

Over Here

Usage

DerivableJS is fairly mature, and has been used enough in production by various people to be considered a solid beta-quality piece of kit.

With React

The fantastic project react-derivable lets you use derivables in your render method, providing seamless interop with component-local state and props.

With Immutable

DerivableJS works spiffingly with Immutable, which is practically required if your app deals with medium-to-large collections.

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

See here

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.