{"id":13517237,"url":"https://github.com/lsm/alfa","last_synced_at":"2026-03-12T06:38:20.490Z","repository":{"id":22540606,"uuid":"95414595","full_name":"lsm/alfa","owner":"lsm","description":"Effortless React State Management.","archived":false,"fork":false,"pushed_at":"2023-01-04T21:38:15.000Z","size":3918,"stargazers_count":85,"open_issues_count":31,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-09T01:20:57.772Z","etag":null,"topics":["alfa","dependency-injection","functional","react","state","subscription"],"latest_commit_sha":null,"homepage":"https://lsm.github.io/alfa","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lsm.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-26T06:17:05.000Z","updated_at":"2023-08-23T15:50:28.000Z","dependencies_parsed_at":"2023-01-13T22:03:38.698Z","dependency_job_id":null,"html_url":"https://github.com/lsm/alfa","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/lsm/alfa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsm%2Falfa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsm%2Falfa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsm%2Falfa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsm%2Falfa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lsm","download_url":"https://codeload.github.com/lsm/alfa/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsm%2Falfa/sbom","scorecard":{"id":601050,"data":{"date":"2025-08-11","repo":{"name":"github.com/lsm/alfa","commit":"47f4ee77320464f5f2f845e3eb0f67c9a236f952"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/9 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 25 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"152 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-whgm-jr23-g3j9","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-c6rq-rjc2-86v2","Warn: Project is vulnerable to: GHSA-257v-vj4p-3w2h","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-p28h-cc7q-c4fg","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-3wcq-x3mq-6r9p","Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm","Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-3gx7-xhv7-5mx3","Warn: Project is vulnerable to: GHSA-6h5x-7c5m-7cr7","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-74fj-2j2h-c42q","Warn: Project is vulnerable to: GHSA-pw2r-vq6v-hr8c","Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc","Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-pfq8-rq6v-vf5m","Warn: Project is vulnerable to: GHSA-6x33-pw7p-hmpq","Warn: Project is vulnerable to: GHSA-c7qv-q95q-8v27","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-7r28-3m3f-r2pr","Warn: Project is vulnerable to: GHSA-r8j5-h5cx-65gg","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-f9cm-qmx5-m98h","Warn: Project is vulnerable to: GHSA-7wpw-2hjm-89gp","Warn: Project is vulnerable to: GHSA-r6rj-9ch6-g264","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-92xj-mqp7-vmcj","Warn: Project is vulnerable to: GHSA-wxgw-qj99-44c2","Warn: Project is vulnerable to: GHSA-5rrq-pxf6-6jx5","Warn: Project is vulnerable to: GHSA-8fr3-hfg3-gpgp","Warn: Project is vulnerable to: GHSA-gf8q-jrpm-jvxq","Warn: Project is vulnerable to: GHSA-2r2c-g63r-vccr","Warn: Project is vulnerable to: GHSA-cfm4-qjh2-4765","Warn: Project is vulnerable to: GHSA-x4jg-mjrx-434g","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-5q6m-3h65-w53x","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-h9rv-jmmf-4pgx","Warn: Project is vulnerable to: GHSA-hxcc-f52p-wc94","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-c9g6-9335-x697","Warn: Project is vulnerable to: GHSA-vx3p-948g-6vhq","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-46c4-8wrp-j99v","Warn: Project is vulnerable to: GHSA-9m6j-fcg5-2442","Warn: Project is vulnerable to: GHSA-hh27-ffr2-f2jc","Warn: Project is vulnerable to: GHSA-rqff-837h-mm52","Warn: Project is vulnerable to: GHSA-8v38-pw62-9cw2","Warn: Project is vulnerable to: GHSA-hgjh-723h-mx2j","Warn: Project is vulnerable to: GHSA-jf5r-8hm2-f872","Warn: Project is vulnerable to: GHSA-wr3j-pwj9-hqq6","Warn: Project is vulnerable to: GHSA-cf66-xwfp-gvc4","Warn: Project is vulnerable to: GHSA-4v9v-hfq4-rm2v","Warn: Project is vulnerable to: GHSA-9jgg-88mc-972h","Warn: Project is vulnerable to: GHSA-g78m-2chm-r7qv","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-282f-qqgm-c34q","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-332q-7ff2-57h2","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T00:25:52.104Z","repository_id":22540606,"created_at":"2025-08-21T00:25:52.104Z","updated_at":"2025-08-21T00:25:52.104Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30417514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T04:41:02.746Z","status":"ssl_error","status_checked_at":"2026-03-12T04:40:12.571Z","response_time":114,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["alfa","dependency-injection","functional","react","state","subscription"],"created_at":"2024-08-01T05:01:31.485Z","updated_at":"2026-03-12T06:38:20.473Z","avatar_url":"https://github.com/lsm.png","language":"JavaScript","readme":"# Alfa\n\n\u003e Effortless React State Management.\n\n[![CI status][ci-img]][ci-url]\n[![License MIT][license-img]][license-url]\n[![NPM version][npm-img]][npm-url]\n[![Dependencies][dep-image]][dep-url]\n[![Coverage Status][coverage-img]][coverage-url]\n[![Code Climate][climate-img]][climate-url]\n\n![Alfa - Effortless React State Management](https://raw.githubusercontent.com/lsm/alfa/master/docs/alfa.gif)\n\n## Why Alfa?\n\n[React](https://facebook.github.io/react/) is an excellent library for creating interactive and stateful views. However, things become unclear when you need to `share \u0026 change data across components`.\n\nAlfa offers an **intuitive and straightforward way** to manage React state. It completely decouples the complex relationships between components and lets you focus on making **components that work anywhere**.\n\nIts **simple** design allows you to adopt it in a matter of minutes while at the same time provides your essential tools to keep your application code easy to change and understand. Here is a list of things why it is the perfect fit for your next React app:\n\n- **Easy** \u0026ndash; Only 4 functions/APIs to learn.\n- **Fast** \u0026ndash; Alfa wraps your components with a thin layer. It introduces little to no performance impacts.\n- **Small** \u0026ndash; ~190LOC \u0026 3KB minified + gzipped.\n- **Async** \u0026ndash; Alfa supports asynchronous operations natively without additional packages.\n- **Explicit** \u0026ndash; Alfa lets you know what a component requires **(input)** and what it changes **(output)**.\n- **Transparent** \u0026ndash; You can use and unit test your components as they are without Alfa. Users of your lib/component could but don't have to use Alfa at all.\n- **React Native** \u0026ndash; Supports React Native out of the box.\n- **Server Render** \u0026ndash; Supports isomorphic apps out of the box.\n- **Production Ready** \u0026ndash; 100% test coverage and being used in productions.\n\n## Links\n\n- [Documentation](https://lsm.github.io/alfa)\n- [Hello World Example](https://github.com/lsm/alfa/tree/master/examples/hello)\n- [TodoMVC Example](https://github.com/lsm/alfa/tree/master/examples/todomvc)\n\n## Quick Guide\n\n### Installation\n\nUse `npm` to add it to your `package.json`.\n\n```sh\nnpm install --save alfa\n```\n\nAlternatively, use `yarn` if you prefer:\n\n```sh\nyarn add alfa\n```\n\n### Getting Data for Components\n\nAlfa converts your regular React component into a **dependency injected component** by `injecting` application data from a key/value store. Let Alfa handle the data if you use it in different components:\n\n```jsx\n// hello.js\nimport React from 'react'\nimport { subscribe } from 'alfa'\n\n// A stateless functional component.\nfunction HelloMessage(props) {\n  // Data is injected as the property of props.\n  return \u003cdiv\u003eHello ${props.name}!\u003c/div\u003e\n}\n\nexport default subscribe(HelloMessage, ['name'])\n```\n\n`subscribe` makes a new component which gets the variable `name` and feeds it into the `HelloMessage` as `props.name` on (re-)rendering.\n\nNow let's see how to use the above component in our app:\n\n```jsx\n// index.js\nimport React from 'react'\nimport { render } from 'react-dom'\nimport { provide, subscribe } from 'alfa'\n\nimport HelloMessage from './hello.js'\n\n// Define the root app which renders HelloMessage as a child.\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cHelloMessage /\u003e\n  \u003c/div\u003e\n)\n\n// Create the Root component by wrapping the App component with initial data\n// using `provide(Component, data)`.\nconst Root = provide(App, { name: 'Motoko' })\n\n// Render it!\nrender(\u003cRoot /\u003e, document.getElementById('root'))\n```\n\nYou don't need to pass the variable `name` to `HelloMessage` as Alfa gets it from the store and passes it to `HelloMessage` component automatically. It allows us to quickly move the component around without worrying about how to get the data it depends on.\n\n### Changing Data\n\nThe simplest way to modify the data of the Alfa store is to inject the built-in `set` function to the component.\n\n```jsx\n// change.js\nimport { subscribe } from 'alfa'\nimport React, { Component } from 'react'\n\n// A stateful class component.\nclass ChangeName extends Component {\n  handleChange = event =\u003e {\n    // Calling `set('mykey', 'my value')` will change the data `mykey`\n    // in store to value `my value`.\n    this.props.set('name', event.target.value)\n  }\n\n  handleSubmit = event =\u003e {\n    event.preventDefault()\n  }\n\n  render() {\n    return (\n      \u003cform onSubmit={this.handleSubmit}\u003e\n        \u003clabel\u003e\n          Name:\n          \u003cinput\n            type=\"text\"\n            value={this.props.name}\n            onChange={this.handleChange}\n          /\u003e\n        \u003c/label\u003e\n      \u003c/form\u003e\n    )\n  }\n}\n\nexport default subscribe(ChangeName, ['set', 'name'], ['name'])\n```\n\nAs mentioned earlier, Alfa makes things explicit. So we need to define the `output` (the 3rd argument when calling the `subscribe` function) of the component explicitly if we want to change a value of a key in the data store. Otherwise, Alfa complains that we are trying to use `set` without defining the correct `output`.\n\nNow add the `ChangeName` component to `App` and your `index.js` ends up like this:\n\n```jsx\n// index.js\nimport React from 'react'\nimport { render } from 'react-dom'\nimport { Provider, subscribe } from 'alfa'\n\nimport HelloMessage from './hello.js'\n\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cHelloMessage /\u003e\n    \u003cChangeName /\u003e\n  \u003c/div\u003e\n)\n\n// Alternatively, you can use Provider - the declarative interface of the\n// provide function.\nrender(\n  \u003cProvider data={{ name: 'Motoko' }}\u003e\n    \u003cApp /\u003e\n  \u003c/Provider\u003e,\n  document.getElementById('root')\n)\n```\n\nNow each time you make a change in the input box, React will re-render both `HelloMessage` and `ChangeName` components to reflect the change.\n\nYou can find the finished version of the above example in the folder [`examples/hello`](https://github.com/lsm/alfa/tree/master/examples/hello).\n\nPlease check the documentation for [API](https://lsm.github.io/alfa/#/api) and [advanced usages](https://lsm.github.io/alfa/#/advanced_usage).\n\n# Advanced Usage\n\nThe hello world example we discussed in the [Quick Guide](https://lsm.github.io/alfa/#/?id=quick-guide) introduced 3 functions `set`, [`provide`](/api#providecomponent-data), and [`subscribe`](/api#subscribecomponent-input-output). In this chapter, we cover more advanced usages which help you writing more performant code and organizing them better.\n\n## Inject instead of subscribing\n\nAlfa has another function called [`inject`](/api#injectcomponent-input-output) and here's how you can use it:\n\n```jsx\nImport React from 'react'\nimport { inject } from 'alfa'\n\nclass SomeComponent extends React.Component {\n  render() {\n    return (\u003ch1\u003ethis.props.title\u003c/h1\u003e)\n  }\n}\n\nexport default inject(SomeComponent, ['title'])\n```\n\nAs you may see [`inject`](/api#injectcomponent-input-output) has the same interface as [`subscribe`](/api#subscribecomponent-input-output). The only difference is changing the value of the injected key (`title` in this case) does not trigger the rerender of the component. Which means it's the perfect choice for static components which won't change after the initial render or dummy components which might be changed only through their parents. Using inject in this case makes your code more performant and use less memory since it does not have to monitor changes of its dependencies like the [`subscribe`](/api#subscribecomponent-input-output) does.\n\n## Action\n\n### What is an Action Function?\n\nIn the [Quick Guide](https://lsm.github.io/alfa/#/?id=changing-data) we learned we could use `set` function to make changes to the store in a component. However, it is common that you want to reuse the same piece of code which uses `set` to change some keys in the store. Then [`action`](/api#actionactionfunction-input-output) is the function to help you create reusable functions that mutate the store which can be used across components. For a large code base, it also helps you to separate the business logic from you representation code and let you understand what component is doing what. (e.g., if a component needs an action called `addTodo` then it probably can add a new todo). An action function is a function that give you the ability to read from and write to the store.\n\n### How to Define an Action?\n\nTo define an action function you need to import the [`action`](/api#actionactionfunction-input-output) function from [`alfa`](https://github.com/lsm/alfa) first, then call it with your original function and the input/output desired.\n\n```js\nimport { action } from 'alfa'\n\nlet actionFunction = action(someFunction, inputs, outputs)\n```\n\nHere is an example from the [TodoMVC example](https://github.com/lsm/alfa/blob/master/examples/todomvc/src/actions/index.js):\n\n```jsx\nimport { action } from 'alfa'\n\n// Action functions only take one argument which must be an object.\nfunction addToDoAction({ text, todos }) {\n  var resultTodos\n\n  if (!text) {\n    // No `text` then we change nothing. The new todos will be the old one.\n    // This will not trigger rerender of the components which subscribe to the\n    // `todos` key.\n\n    resultTodos = todos\n  } else {\n    // We have `text` then construct a new todo item using it and make a new\n    // todos array out of the old one. This causes rerender if you return it\n    // (see the return statement at the end of this function).\n\n    resultTodos = todos.concat([\n      {\n        id: Math.random(),\n        text: text,\n        completed: false\n      }\n    ])\n  }\n\n  // Now replace the new todos with the old one and return it.\n  // Return of action function must be an object.\n\n  return {\n    todos: resultTodos\n  }\n}\n\n// `action` takes three arguments: the action function, inputs and outputs.\n// It returns the `addToDoAction` function with a new property `alfaAction`\n// attached. The `alfaAction` function is a curry function which takes a `store`\n// instance and returns a `dependency-injected` version of the `addToDoAction`\n// function.\n\nexport const addToDo = action(addToDoAction, ['text', 'todos'], 'todos')\n```\n\nLike the design of `inject/subscribe`, [`action`](/api#actionactionfunction-input-output) make it explicit about what are the input and output of the action function. Alfa enforces the output part and throws an error if you return something that is not predefined in the output:\n\n```js\naction(\n  function({ input1 }) {\n    return {\n      // This is a legit output.\n      output1: 'some result',\n\n      // This causes alfa to throw an error.\n      randomOutput: 'some value'\n    }\n  },\n  'input1',\n  'output1'\n)\n```\n\n### Asynchronous Action\n\nFor asynchronous operations we can use `set` in the action function to change the store just like what we have done in the [Quick Guide](https://lsm.github.io/alfa/#/?id=changing-data):\n\n```jsx\nfunction deleteToDoAction({ set, todos, todoID }) {\n  var resultTodos = todos.filter(function(todo) {\n    return todoID !== todo.id\n  })\n\n  // Simulate an async operation.\n  setTimeout(function() {\n    set({ todos: resultTodos })\n    // You can call `set` as many times as you want.\n    set('todosCount', todos.length)\n  }, 100)\n}\n\nexport const deleteTodo = action(\n  deleteToDoAction,\n  ['todoID', 'todos'],\n  ['todos', 'todosCount']\n)\n```\n\nSince the purpose of the action functions is making changes to the store, so the `set` function is `pre-injected` - you don't have to define it as an input of the action function.\n\n### How to Use Action in Components?\n\nTo use the actions we first need to set them in the store. Alfa then binds the function with the instance of the store, so you don't have to worry about which store it works with when you call the action functions.\n\n```jsx\nimport React from 'react'\nimport { Provider } from 'alfa'\n\nimport TodoApp from './App'\nimport * as todoActions from './todo-actions'\n\n/**\n * Initial state of applicaiton.\n */\nconst data = {\n  // Actions must be first level keys.\n  addToDo: todoActions.addToDo,\n\n  deleteToDo: todoActions.deleteToDo\n\n  // Yeah, we can simply use the spread syntax:\n  ...todoActions\n}\n\n/**\n * Render our application.\n */\nrender(\n  \u003cProvider data={data}\u003e\n    \u003cTodoApp /\u003e\n  \u003c/Provider\u003e,\n  document.getElementById('root')\n)\n```\n\nThen in the component, we can get it as a normal dependency:\n\n```jsx\n// ...\n\nclass TodoItem extends Component {\n  // ...\n\n  onDeleteClick(todoID) {\n    // The deleteTodo action also requires `todos` as input, but we don't have to\n    // provide it here as alfa gets it from the store automatically.\n    // Note, the parameters fed into the action here have higher priority\n    // than the one injected from the store. It allows you to override the\n    // input of the action when necessary.\n\n    this.props.deleteTodo({ todoID })\n  }\n\n  // ...\n}\n\n// ...\n\nexport default inject(TodoItem, ['deleteTodo'])\n```\n\n## Dynamic Keys\n\n### What is Dynamic Keys for?\n\nTo make changes to the store, [`alfa`](https://github.com/lsm/alfa) requires you to define the names of the outputs for both injected/subscribed components or action functions. What if the key you want to change is a dynamic value? For example, a unique id for a chat room? You can not predefine the output key in this case because it is data not code. Luckily, [`alfa`](https://github.com/lsm/alfa) supports this use case and it's called `Dynamic Keys`.\n\nAnother use case is when you want to load more dependencies based on the `props` of the component at initialization stage.\n\nTo use it you need to define a function as the static property [`keys`](/api#keysprops) for your component.\n\n### How to Define Dynamic Keys?\n\nLet's see how it works with a concrete example. Assume you have a chat app which supports multiple chat rooms and threading in a certain room. To control the open or the close of the threads you need to keep track of which thread is currently active in which chat room. We can use the combination of `roomID/activeThreadID` as the key to store the state of the active thread. For example, `{ alfadev/activeThreadID: 'thread23' }` means in chat room `alfadev` the current active thread id is `thread23`.\n\n```jsx\n// ...\n\nclass ChatRoom extends Component {\n  // ...\n\n  static keys = props =\u003e {\n    return {\n      chatRoom: props.roomID,\n      // Each room can only have one active thread. If you room id is `alfadev`\n      // then the key for storing active thread id of this room is\n      // `alfadev/activeThreadID`\n      activeThreadID: props.roomID + '/activeThreadID'\n    }\n  }\n\n  // ...\n}\n\n// For functional component, add the `keys` property to the function itself.\n\nfunction ChatRoom(props) {\n  // ...\n}\n\nChatRoom.keys = props =\u003e {\n  return {\n    chatRoom: props.roomID,\n    activeThreadID: props.roomID + '/activeThreadID'\n  }\n}\n```\n\n### How to Use Dynamic Keys in Components?\n\nThe injected props in the component has static names (e.g., `chatRoom` and `activeThreadID`). You can get and set them as normal dependencies:\n\n```js\n// ...\nlet chatRoom = this.props.chatRoom\n// ...\nthis.props.set('activeThreadID', 'thread56')\n// ...\n```\n\nIt helps you write clean and abstract code. Then in our example, a function to toggle the show/hide of the thread looks like this:\n\n```jsx\n// ...\n\nclass ChatRoom extends Component {\n  // ...\n\n  handleThreadToggle = () =\u003e {\n    const { thread, activeThreadID } = this.props\n\n    if (activeThreadID === thread.id) {\n      this.props.set('activeThreadID', null)\n    } else {\n      this.props.set('activeThreadID', thread.id)\n    }\n  }\n\n  // ...\n}\n```\n\nWhen you call `this.props.set('activeThreadID', null)` Alfa maps the dynamic key back to the real key we defined in the return object of the [`keys`](/api#keysprops) function. In this case the `set` function call changes the value of the key `alfadev/activeThreadID` to `null` or the id of the thread.\n\nNow defining the output of the ChatRoom component becomes more intuitive:\n\n```js\nexport default subscribe(\n  ChatRoom,\n  ['set', 'activeThreadID'],\n  ['activeThreadID']\n)\n```\n\nYou can find a full example for the usage of the dynamic keys in [injection.test.js](https://github.com/lsm/alfa/blob/master/test/injection.test.js) test case `Should set dynamic key correctly`.\n\n## Multiple Stores\n\nSometimes you may want to have multiple stores for different sections of your application. It is easy to achieve with [`alfa`](https://github.com/lsm/alfa), all you need to do is put components under separate [`Providers`](/api#provider):\n\n```jsx\nfunction App() {\n  return (\n    \u003cdiv\u003e\n      \u003cProvider data={data1}\u003e\n        \u003cSubApp1 /\u003e\n      \u003c/Provider\u003e\n      \u003cProvider data={data2}\u003e\n        \u003cSubApp2 /\u003e\n      \u003c/Provider\u003e\n    \u003c/div\u003e\n  )\n}\n\nrender(\u003cApp /\u003e, document.getElementById('root'))\n```\n\nNow you won't have any conflicts even you have the same `key` in both `SubApp1` and `SubApp2` since they are under different [`Providers`](/api#provider) and stores.\n\n\n# API\n\n## provide(component, _data_)\n\nProvide a store to the wrapped component and its children components\n\n* **Parameters**\n\n  * `component` - **required** `Function|Class`: Wrapped function or class (React component).\n  * `data` - _optional_ `Object|Store`: Initial data object or instance of `alfa.Store`.\n\n* **Return**\n\n  `Function` - it returns a function which you can use it as a regular React component.\n\n## Provider\n\nThe declarative interface of [`provide`](/api#providecomponent-data) function. It wraps a component and its children with an Alfa store.\n\n```jsx\n// `data` is optional as it is in `provide`.\n\u003cProvider data={Object | Store}\u003e\n  // Only one child is allowed.\n  \u003cSomeComponent /\u003e\n\u003c/Provider\u003e\n```\n\n## inject(component, _input_, _output_)\n\nInject data to the `component` as props based on the keys provided from `input`.\n\n* **Parameters**\n\n  * `component` - **required** `Function|Class`: Wrapped function or class (React component).\n  * `input` - _optional_ `Array|String`: The keys of input dependencies. Could be a single string or array of strings.\n  * `output` - _optional_ `Array|String`: The keys of output dependencies. Could be a single string or array of strings.\n\n* **Return**\n\n  * `Function` - it returns a function which we can use it as a regular React component.\n\n## subscribe(component, _input_, _output_)\n\nSubscribe data to the `component` as props based on the keys provided from `input`. Any changes made to the keys found in `input` triggers rerender of the component.\n\n* **Parameters**\n\n  * `component` - **required** `Function|Class`: Wrapped function or class (React component).\n  * `input` - _optional_ `Array|String`: The keys of input dependencies. Could be a single string or array of strings.\n  * `output` - _optional_ `Array|String`: The keys of output dependencies. Could be a single string or array of strings.\n\n* **Return**\n\n  * `Function` - it returns a function which you can use it as a standard React component.\n\n## action(actionFunction, _input_, _output_)\n\nAttach a curry function to the `actionFunction` which you can use it as `dependency-injected` version of `actionFunction` in components under a `Provider`.\n\n* **Parameters**\n\n  * `actionFunction` - **required** `Function`: The action function that makes changes to the store.\n  * `input` - _optional_ `Array|String`: The keys of input dependencies. Could be a single string or array of strings.\n  * `output` - _optional_ `Array|String`: The keys of output dependencies. Could be a single string or array of strings.\n\n* **Return**\n\n  * `Function` - it returns the original `actionFunction` with a curry function attached as property `alfaAction`.\n\n## keys(props)\n\nWe can define function `keys` as a static property of a component or function to support dynamic names of injected/subscribed props. For subscribed component, Alfa calls it at initialization stage (constructor). For injected component, Alfa calls it every time before it renders. For either case, Alfa always calls it before rendering the wrapped component. Then we have the chance to get more dependencies based on the returns of the `keys` function and merge them with the `props` of the component. Dynamic keys have higher priority than the original properties in props. Which means a dynamic key overrides the value of the property if they are the same.\n\n```js\n  static keys(props) =\u003e {\n    return {\n      userSettings: props.userID + '/settings',\n    }\n    // Or return an array.\n    return ['settings']\n  }\n```\n\n* **Parameters**\n\n  * props - `Object`: the original `props` of the component.\n\n* **Return**\n\n  * `Object|Array` - Object of the mapping between real keys and dynamic keys. Alternatively, an array of real keys. Concrete keys are the actual keys found in the store. Dynamic keys are the property names of the `props` accessible in the component. A dynamic name is like an alias of its corresponding concrete key which only lives in a component.\n\n## Store(_data_)\n\nThe `Store` class. It implements a flat key/value store with the support of subscribing changes of keys with listening functions. You do not need it for most of the cases.\n\n* **Methods**\n  * constrcutor(_data_)\n    * **Parameters**\n      * data - _optional_ `Object`: The initial data for the store.\n  - set(key, _value_)\n    Change a value or values in the store.\n    * **Parameters**\n      * key - **required** `String|Object`: String value of the key. Alternatively, Object of key/value pairs when you need to set multiple values at once.\n      * value - _optional_ `Any`: The value of the key.\n  - get(key)\n    Get value or values from the store.\n    * **Parameters**\n      * key - **required** `String|Array`: String value of the key. Alternatively, an array of keys when you need to retrieve multiple values at once.\n    * **Return**\n      * `Any` the value of the key or an object which contains the key/value pairs if the `key` was an array.\n  - subscribe(keys, listener, _maps_)\n    Call the listening function when `set` was called on any of the `keys`.\n    * **Parameters**\n      * keys - **required** `Array`: Array of keys the listener will be subscribing to.\n      * listener - **required** `Function`: The listener function.\n      * maps - _optional_ `Object`: Optional injection key to real key mappings.\n  - unsubscribe(listener)\n    Unsubscribe the function from all the keys it's listening to.\n    * **Parameters**\n      * listener - **required** `Function`: The listener function to unsubscribe.\n\n# License\n\nMIT\n\n[dep-url]: https://david-dm.org/lsm/alfa\n[dep-image]: https://david-dm.org/lsm/alfa.svg\n[dev-url]: https://david-dm.org/lsm/alfa?type=dev\n[dev-image]: https://david-dm.org/lsm/alfa/dev-status.svg\n[license-img]: https://img.shields.io/badge/License-MIT-green.svg\n[license-url]: http://opensource.org/licenses/MIT\n[npm-img]: https://badge.fury.io/js/alfa.svg\n[down-img]: https://img.shields.io/npm/dm/alfa.svg\n[npm-url]: https://npmjs.org/package/alfa\n[ci-img]: https://circleci.com/gh/lsm/alfa/tree/master.svg?style=shield\n[ci-url]: https://circleci.com/gh/lsm/alfa/tree/master\n[travis-img]: https://travis-ci.org/lsm/alfa.svg?branch=master\n[travis-url]: http://travis-ci.org/lsm/alfa\n[coverage-img]: https://coveralls.io/repos/github/lsm/alfa/badge.svg?branch=master\n[coverage-url]: https://coveralls.io/github/lsm/alfa?branch=master\n[climate-img]: https://codeclimate.com/github/lsm/alfa/badges/gpa.svg\n[climate-url]: https://codeclimate.com/github/lsm/alfa\n[saucelabs-url]: https://saucelabs.com/browser-matrix/alfajs.svg\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsm%2Falfa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flsm%2Falfa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsm%2Falfa/lists"}