{"id":13780591,"url":"https://github.com/ds300/derivablejs","last_synced_at":"2025-04-12T23:29:50.404Z","repository":{"id":34625079,"uuid":"38574577","full_name":"ds300/derivablejs","owner":"ds300","description":"Functional Reactive State for JavaScript and TypeScript","archived":false,"fork":false,"pushed_at":"2018-05-26T10:21:10.000Z","size":2218,"stargazers_count":514,"open_issues_count":11,"forks_count":23,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-04-28T18:35:29.176Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ds300.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-05T15:51:49.000Z","updated_at":"2024-01-12T14:46:17.000Z","dependencies_parsed_at":"2022-07-15T17:30:42.800Z","dependency_job_id":null,"html_url":"https://github.com/ds300/derivablejs","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ds300%2Fderivablejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ds300%2Fderivablejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ds300%2Fderivablejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ds300%2Fderivablejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ds300","download_url":"https://codeload.github.com/ds300/derivablejs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647064,"owners_count":21139080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T18:01:17.589Z","updated_at":"2025-04-12T23:29:50.383Z","avatar_url":"https://github.com/ds300.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003eDerivableJS\u003c/h1\u003e\n\u003ch3 align=\"center\"\u003eState made simple → Effects made easy\u003c/h3\u003e\n\n## [![npm](https://img.shields.io/npm/v/derivable.svg?maxAge=2592000)](https://www.npmjs.com/package/derivable) [![Build Status](https://travis-ci.org/ds300/derivablejs.svg?branch=new-algo)](https://travis-ci.org/ds300/derivablejs) [![Coverage Status](https://coveralls.io/repos/github/ds300/derivablejs/badge.svg?branch=new-algo)](https://coveralls.io/github/ds300/derivablejs?branch=new-algo) [![Join the chat at https://gitter.im/ds300/derivablejs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ds300/derivablejs?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge) [![Empowered by Futurice's open source sponsorship program](https://img.shields.io/badge/sponsor-chilicorn-ff69b4.svg)](http://futurice.com/blog/sponsoring-free-time-open-source-activities?utm_source=github\u0026utm_medium=spice\u0026utm_campaign=derivablejs) [![.min.gz size](https://img.shields.io/badge/.min.gz%20size-3.4k-blue.svg)](http://github.com)\n\nDerivables are an Observable-like state container with superpowers. Think\n[MobX](https://github.com/mobxjs/mobx) distilled to a potent essence, served\nwith two heaped tablespoons of extra performance, a garnish of declarative\neffects management, and a healthy side-salad of immutability.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n* [Quick start](#quick-start)\n* [Reactors](#reactors)\n* [Key differences with MobX](#key-differences-with-mobx)\n* [API / Documentation](#api--documentation)\n* [Usage](#usage)\n  * [With React](#with-react)\n  * [With Immutable](#with-immutable)\n  * [Debugging](#debugging)\n  * [Examples](#examples)\n* [Contributing](#contributing)\n* [Inspiration \u003c3](#inspiration-3)\n* [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Quick start\n\nThere are two types of Derivable:\n\n* **Atoms**\n\n  Atoms are simple mutable references to immutable values. They represent the\n  ground truth from which all else is derived.\n\n  ```js\n  import { atom } from \"derivable\";\n\n  const name = atom(\"Richard\");\n\n  name.get(); // =\u003e 'Richard'\n\n  name.set(\"William\");\n\n  name.get(); // =\u003e 'William'\n  ```\n\n* **Derivations**\n\n  Derivations are declarative transformations of values held in atoms. You can\n  create them with the `derive` function.\n\n  ```js\n  import { derive } from \"derivable\";\n\n  const cyber = word =\u003e\n    word\n      .toUpperCase()\n      .split(\"\")\n      .join(\" \");\n\n  const cyberName = derive(() =\u003e cyber(name.get()));\n\n  cyberName.get(); // 'W I L L I A M'\n\n  name.set(\"Sarah\");\n\n  cyberName.get(); // 'S A R A H'\n  ```\n\n  Unlike atoms, derivations cannot be modified in-place with a `.set` method.\n  Their values change only when one or more of the values that they depend upon\n  change. Here is an example with two dependencies.\n\n  ```js\n  const transformer = atom(cyber);\n\n  const transformedName = derive(() =\u003e transformer.get()(name.get()));\n\n  transformedName.get(); // =\u003e 'S A R A H'\n\n  const reverse = string =\u003e\n    string\n      .split(\"\")\n      .reverse()\n      .join(\"\");\n\n  transformer.set(reverse);\n\n  transformedName.get(); // =\u003e 'haraS'\n\n  name.set(\"Fabian\");\n\n  transformedName.get(); // =\u003e 'naibaF'\n  ```\n\n  `derive` takes a function of zero arguments which should dereference one or\n  more Derivables to compute the new derived value. DerivableJS then sneakily\n  monitors who is dereferencing who to infer the parent-child relationships.\n\n## Reactors\n\nDeclarative state management is nice in and of itself, but the real benefits\ncome from how it enables us to more effectively manage side effects. DerivableJS\nhas a really nice story on this front: changes in atoms or derivations can be\nmonitored by things called **Reactors**, which do not themselves have any kind\nof 'current value', but are more like independent agents which exist solely for\nexecuting side effects.\n\nLet's have a look at a tiny example app which greets the user:\n\n```js\nimport { atom, derive, transact } from \"derivable\";\n\n// global application state\nconst name = atom(\"World\"); // the name of the user\nconst countryCode = atom(\"en\"); // for i18n\n\n// static constants don't need to be wrapped\nconst greetings = {\n  en: \"Hello\",\n  de: \"Hallo\",\n  es: \"Hola\",\n  cn: \"您好\",\n  fr: \"Bonjour\"\n};\n\n// derive a greeting message based on the user's name and country.\nconst greeting = derive(() =\u003e greetings[countryCode.get()]);\nconst message = derive(() =\u003e `${greeting.get()}, ${name.get()}!`);\n\n// set up a Reactor to print the message every time it changes, as long as\n// we know how to greet people in the current country.\nmessage.react(msg =\u003e console.log(msg), { when: greeting });\n// $\u003e Hello, World!\n\ncountryCode.set(\"de\");\n// $\u003e Hallo, World!\nname.set(\"Dagmar\");\n// $\u003e Hallo, Dagmar!\n\n// we can avoid unwanted intermediate reactions by using transactions\ntransact(() =\u003e {\n  countryCode.set(\"fr\");\n  name.set(\"Étienne\");\n});\n// $\u003e Bonjour, Étienne!\n\n// if we set the country code to a country whose greeting we don't know,\n// `greeting` becomes undefined, so the `message` reactor won't run\n// In fact, the value of `message` won't even be recomputed.\ncountryCode.set(\"dk\");\n// ... crickets chirping\n```\n\nThe structure of this example can be depicted as the following DAG:\n\n\u003cimg src=\"https://ds300.github.com/derivablejs/img/example.svg\" align=\"center\" width=\"89%\"/\u003e\n\n## Key differences with MobX\n\n* Smaller API surface area.\n\n  There are far fewer _kinds of thing_ in DerivableJS, and therefore fewer\n  things to learn and fewer surprising exceptions and spooky corners. This\n  reduces noise and enhances one's ability to grok the concepts and wield the\n  tools on offer. It also shrinks the set of tools on offer, but maybe that's\n  not a bad thing:\n\n  \u003e It seems that perfection is attained not when there is nothing more to add,\n  \u003e but when there is nothing more to remove.\n\n  _- Antoie de Saint Exupéry_\n\n* No transparent dereferencing and assignment.\n\n  It is always necessary to call `.get` on derivables to find out what's inside,\n  and you always have to call `.set` on atoms to change what's inside. This\n  provides a consistent semantic and visual distinction between ordinary values\n  and derivable values.\n\n* No observable map and array types.\n\n  So you probably have to use something extra like Immutable,\n  [icepick](https://github.com/aearly/icepick) or\n  [pure javascript immutable arrays](https://vincent.billey.me/pure-javascript-immutable-array/)\n  to deal with collections. Not great if you're just out to get shit done fast,\n  but the benefits of immutable collections become more and more valuable as\n  projects mature and grow in scope.\n\n* More subtle control over reactors\n\n  DerivableJS has a tidy and flexible declarative system for defining when\n  reactors should start and stop. This is rather nice to use for managing many\n  kinds of side effects.\n\n* Speed\n\n  DerivableJS is finely tuned, and propagates change significantly faster than\n  MobX. \\[link to benchmark-results.html forthcoming\\]\n\n## API / Documentation\n\n[Over Here](https://ds300.github.io/derivablejs)\n\n## Usage\n\nDerivableJS is fairly mature, and has been used enough in production by various\npeople to be considered a solid beta-quality piece of kit.\n\n### With React\n\nThe fantastic project\n[react-derivable](https://github.com/andreypopp/react-derivable) lets you use\nderivables in your render method, providing seamless interop with\ncomponent-local state and props.\n\n### With Immutable\n\nDerivableJS works spiffingly with\n[Immutable](https://github.com/facebook/immutable), which is practically\nrequired if your app deals with medium-to-large collections.\n\n### Debugging\n\nDue to inversion of control, the stack traces you get when your derivations\nthrow errors can be totally unhelpful. There is a nice way to solve this problem\nfor dev time. See\n[setDebugMode](https://ds300.github.com/derivablejs/#derivable-setDebugMode) for\nmore info.\n\n### Examples\n\n[See here](https://github.com/ds300/derivablejs/tree/master/examples)\n\n## Contributing\n\nI heartily welcome questions, feature requests, bug reports, and general\nsuggestions/criticism on the github issue tracker. I also welcome bugfixes via\npull request (please read CONTRIBUTING.md before sumbmitting).\n\n## Inspiration \u003c3\n\n* [Are we there yet?](https://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey)\n* The [re-frame README](https://github.com/Day8/re-frame)\n* [ratom.cljs](https://github.com/reagent-project/reagent/blob/master/src/reagent/ratom.cljs)\n* [Turning the database inside out](https://www.youtube.com/watch?v=fU9hR3kiOK0)\n* [Simple Made Easy](https://www.infoq.com/presentations/Simple-Made-Easy)\n\n## License\n\n```\nCopyright 2015 David Sheldrick \u003cdjsheldrick@gmail.com\u003e\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":[],"categories":["List","JavaScript","Libraries","Libraries Built with RxJS"],"sub_categories":["Data Structures"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fds300%2Fderivablejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fds300%2Fderivablejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fds300%2Fderivablejs/lists"}