{"id":15715801,"url":"https://github.com/gnapse/keflux","last_synced_at":"2025-05-12T23:53:26.182Z","repository":{"id":34514662,"uuid":"38456434","full_name":"gnapse/keflux","owner":"gnapse","description":"Flux workflow using Kefir's FRP streams","archived":false,"fork":false,"pushed_at":"2015-07-02T21:40:03.000Z","size":120,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-05-12T23:53:18.778Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/keflux","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gnapse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-02T20:54:01.000Z","updated_at":"2016-10-14T08:13:49.000Z","dependencies_parsed_at":"2022-09-15T04:02:24.128Z","dependency_job_id":null,"html_url":"https://github.com/gnapse/keflux","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnapse%2Fkeflux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnapse%2Fkeflux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnapse%2Fkeflux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnapse%2Fkeflux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gnapse","download_url":"https://codeload.github.com/gnapse/keflux/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253843185,"owners_count":21972870,"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-10-03T21:43:08.254Z","updated_at":"2025-05-12T23:53:26.142Z","avatar_url":"https://github.com/gnapse.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Keflux\n\n**Keflux** is a library that allows to use [event streams][] to build a\n[Flux][] architecture in a front-end [React][] app.  It uses [Kefir][] for all\nFRP-related code, and as part of the library name.  It also embraces the use of\n[immutable][] data structures, as an integral part of how it is designed, and\nhow it works.\n\n[event streams]: https://en.wikipedia.org/wiki/Functional_reactive_programming\n[Flux]: https://facebook.github.io/flux/\n[React]: https://facebook.github.io/react/\n[Kefir]: https://rpominov.github.io/kefir/\n[immutable]: https://facebook.github.io/immutable-js/\n\n## Stores\n\nKeflux promotes the concept of self-contained stores, each defining its data\nstructure and the actions that are available to perform on it.  Below is an\nexample store for a todo-list app:\n\n```javascript\nconst TodoStore = Keflux.Store({\n\n  create(stream) {\n    return stream.\n      filter((text) =\u003e text.trim().length \u003e 0).\n      map((text) =\u003e {\n        const todo = Immutable.OrderedMap({\n          id: uuid.v1(),\n          text: text,\n          completed: false,\n        });\n        return (data) =\u003e data.set(todo.get(\"id\"), todo);\n      });\n  },\n\n  updateText(stream) {\n    return stream.\n      filter((params) =\u003e params.text.trim().length \u003e 0).\n      map((params) =\u003e {\n        return (data) =\u003e data.setIn([params.id, \"text\"], params.text);\n      });\n  },\n\n  toggleComplete(stream) {\n    return stream.map((todo) =\u003e {\n      return (data) =\u003e data.setIn([todo.id, \"completed\"], !todo.completed);\n    });\n  },\n\n  destroy(stream) {\n    return stream.map((todo) =\u003e {\n      return (data) =\u003e data.remove(todo.id);\n    });\n  },\n\n  clearCompleted(stream) {\n    return stream.map(() =\u003e {\n      return (data) =\u003e data.filter((todo) =\u003e !todo.get('completed'));\n    });\n  },\n\n  toggleAll(stream) {\n    return stream.map((checked) =\u003e {\n      return (data) =\u003e data.map((todo) =\u003e todo.set('completed', checked));\n    });\n  },\n\n});\n```\n\nEach action method receives a stream of events, each event being an invocation\nby the app to trigger that particular action.  These action methods can then\ntransform this stream using traditional FRP functions (filter, map, etc.)\n\nThe resulting stream should issue a series of functions, that when applied to\nthe underlying data structure, modifies it in a way consistent with the action\ninvoked.\n\nFor more real-world-like examples, take a look to a couple of stores in the\n`examples/` directory.  Those are todo-list stores but with persistend storage\n(one to local storage, and the other to an ajax json api).\n\n### Understanding how it works\n\nTo better understand this concept, let's look in detail at the `create` action\nfrom the example above:\n\n```javascript\ncreate(stream) {\n  return stream.\n    filter((text) =\u003e text.trim().length \u003e 0).\n    map((text) =\u003e {\n      const todo = Immutable.OrderedMap({\n        id: uuid.v1(),\n        text: text,\n        completed: false,\n      });\n      return (data) =\u003e data.set(todo.get(\"id\"), todo);\n    });\n}\n```\n\nThe `stream` received as argument above will emit a new value each time the app\ninvokes this action on the store.  The first thing the action does is filtering\nout those invocations made with an empty text, since we do not want to-do items\nwhose text is an empty string.\n\nThen the valid events (those with non-empty text) are mapped to a function that\nreceives the underlying data structure of the store (an [immutable map][]), and\nreturns a copy of it, but with the new todo-item added to it.\n\n[immutable map]: facebook.github.io/immutable-js/docs/#/Map\n\nThe result is a new stream of functions, that when applied to the store's data\nstructure, each add a new todo item to it, as requested by the app using this\nstore.  The store takes care of applying these functions to the underlying data\nstructure, on each iteration of the stream processing.\n\n## Usage on a React.js app\n\nKeflux stores expose a stream called `changes`, which emits a new value each\ntime the underlying data structure changes as a result of triggering one of its\nactions.\n\nSo once stores are defined like it is described above, a React.js component can\nsubcribe to the `changes` on this store, and update its state to match the\nstore's data.  This is shown in the following example:\n\n```javascript\nconst TodoApp = React.createClass({\n  propTypes: {\n    store: React.PropTypes.object,\n  },\n\n  getInitialState() {\n    return {\n      data: this.props.store.data,\n    };\n  },\n\n  componentDidMount() {\n    this.props.store.changes.onValue((data) =\u003e this.setState({data}));\n  },\n\n  render() {\n    // ...\n  },\n});\n```\n\nThere are plans to provide a mixin for components to easily declare that its\nstate depends on one or more stores, as well as to take advantage of [React.js'\ncontexts][] to implicitly pass stores to child components.\n\n## What's next\n\nThis is a list of features that have not been explored or implemented yet.\n\n* Automated tests.\n* How does error handling fits with this architecture.\n* Mixin for easing the access to stores/actions in components via [React.js' contexts][].\n* Registering callbacks to handle action completion.\n\n[React.js' contexts]: https://blog.jscrambler.com/react-js-communication-between-components-with-contexts/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnapse%2Fkeflux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgnapse%2Fkeflux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnapse%2Fkeflux/lists"}