{"id":16225425,"url":"https://github.com/mikebild/reactish-elmish","last_synced_at":"2025-03-19T12:30:57.923Z","repository":{"id":136939835,"uuid":"69908749","full_name":"MikeBild/reactish-elmish","owner":"MikeBild","description":"Proof of Concept - RxJS + React -\u003e Elmish","archived":false,"fork":false,"pushed_at":"2017-06-07T06:27:00.000Z","size":214,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-28T18:42:49.278Z","etag":null,"topics":["component","design","elm","proof-of-concept","react","rxjs"],"latest_commit_sha":null,"homepage":"https://reactish-elmish.cloud.dropstack.run","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/MikeBild.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-03T20:34:50.000Z","updated_at":"2021-02-04T21:02:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"ff6d8423-8308-4f0d-b990-de63a90f3296","html_url":"https://github.com/MikeBild/reactish-elmish","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/MikeBild%2Freactish-elmish","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MikeBild%2Freactish-elmish/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MikeBild%2Freactish-elmish/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MikeBild%2Freactish-elmish/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MikeBild","download_url":"https://codeload.github.com/MikeBild/reactish-elmish/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243989576,"owners_count":20379648,"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":["component","design","elm","proof-of-concept","react","rxjs"],"created_at":"2024-10-10T12:45:00.292Z","updated_at":"2025-03-19T12:30:57.917Z","avatar_url":"https://github.com/MikeBild.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React + RxJS -\u003e Elmish\n\n\u003e __Proof of Concept__ - [Rx(ified)](http://reactivex.io/rxjs/) and [React(ified)](https://facebook.github.io/react/docs/introducing-jsx.html) version for a better understanding of the [Elm Architecture](https://guide.elm-lang.org/architecture/). Don't use in production!\n\n## [Live Demo](https://reactish-elmish.cloud.dropstack.run)\n\n## Setup\n\n```bash\nnpm install\nnpm test\nnpm run dev\n```\n\n## Build\n\n```bash\nnpm run build\n```\n\n## Deploy to [https://dropstack.run](https://dropstack.run)\n\n```bash\nnpm run deploy\n```\n\n## Flow\n\n![Diagram](docs/diagram.png)\n\n## Introduction\n\n* Frontend-Developer friendly syntax and semantic (similar to Event/Game-Loop)\n* Fits for \"smart\" components (side effects + state management + logic + rendering)\n* Stateless React Functions for \"dumb\" components\n* Component based state and side effect management (avoid central state)\n* Minimal inversive - combine React-Rendering and Reactive Extentions without building a Framework with unnecessary abstractions\n* Follow the principle of \"reactive\" fold left with [Rx Scan Operator](http://rxmarbles.com/#scan)\n* Async/Sync side effect orchestration via [Rx-Observables](http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html)\n\n## Reative Extentions vs. Elm Side-Effects\n\n* Subscriptions in Elm signals **\"only\" next message**. No publishing of error and complete signals. Just use a **\"message content filter\"** in model update functions.\n* To handle and chain asynchronous side-effects Elm have the concept of [Tasks](http://package.elm-lang.org/packages/elm-lang/core/latest/Task)\n  * Tasks have success and error signals\n  * You can chain your side-effects with `task.andThen`\n\n## Handle side-effects and compose asynchron data flows\n\n* Reactish-Elmish manage side-effects using observable from the `subscription(...)` function\n* Observables are streams of messages\n* Observables are decomposable to smaller observables\n* Observables are composable to larger observables\n* Every observable has three different kinds of \"signals\" to manage the lifecycle (subscription state) of the stream\n  * next - data message pushed to the stream\n  * error - error message pushed to the stream (end of subscription)\n  * complete - done message pushed to the stream (end of subscription)\n\n### Creation of initial stream\n\n* Return an observable (e.g. Rx.Observable.interval(...) in your component using the `subscription(cmd, $observable)` function\n* You don't need to `subscribe()` inside of your `subscription(...)` function. Just, send a command and Reactish-Elmish will `subscribe(...)` to your created observable.\n* Reactish-Elmish calls `update(message, ...)` via `$observable.do(...)` for every message pushed from the created observable\n\n### Handle the stream lifecycle\n\n* You get your created observable reference to the second function argument [**(message, $observable) -\u003e $observable)**]\n* Decompose to smaller data flows via observable operators like (.filter(...), .take(...), etc.)\n* Compose to larger data flows via observable operators like (.merge(...), .concat(...), etc.)\n* Compose with other observables to manage the subscription lifecycle using (.takeUntil(...), .takeWhile(...), etc.)\n\n## Examples\n\n* [All in One](http://reactish-elmish.cloud.dropstack.run/allinone)\n* [Counter](http://reactish-elmish.cloud.dropstack.run/counter)\n* [Counter delayed](http://reactish-elmish.cloud.dropstack.run/counterdelayed)\n* [Counter composed](http://reactish-elmish.cloud.dropstack.run/countercomposed)\n* [Form / Validation](http://reactish-elmish.cloud.dropstack.run/form)\n* [Interval component](http://reactish-elmish.cloud.dropstack.run/intervalcomponent)\n* [Interval component via functional composition](http://reactish-elmish.cloud.dropstack.run/intervalcomponentcompose)\n* [Random Gif (Fetch)](http://reactish-elmish.cloud.dropstack.run/randomgiffetch)\n* [Random Gif (Rx)](http://reactish-elmish.cloud.dropstack.run/randomgifrx)\n* [Optimistic UI updates](http://reactish-elmish.cloud.dropstack.run/optimisticupdate)\n* [Long-Running Process (aka Saga) (RxJs)](http://reactish-elmish.cloud.dropstack.run/ordersaga)\n* [CouchDB/PouchDB change feed via AJAX long polling](http://reactish-elmish.cloud.dropstack.run/webevents)\n* [EventSource via HTTP/2 push notifications](http://reactish-elmish.cloud.dropstack.run/http2eventsource)\n* [Inter-component communication via global/parent state handling (aka createStore/reducer)](http://reactish-elmish.cloud.dropstack.run/componentcommunication)\n* [Inter-component communication via streams (RxJs)](http://reactish-elmish.cloud.dropstack.run/componentcommunicationviastream)\n* [Component composition with GraphQL data fetching (Apollo-Client)](http://reactish-elmish.cloud.dropstack.run/componentwithgraphql)\n\n## Elmish API\n\n**init(props)** function, returning the initial model (a state is an object with a required key model and an optional key cmd) [**() -\u003e model**]\n\n**update(model, message)** reduce function, returns the new model [**(model, message) -\u003e model**]\n\n**view(model, action)** function, returns the updated UI (React)  [**(model) -\u003e html**]\n\n**subscription(cmd, $observable)** function, manage composable side effects returns an Rx observable (stream of messages) [**(message, $observable) -\u003e $observable)**]\n\n## Reactish API\n\n### Use Reactish-Elmish with functional composition (compose / withElmish)\n\n```javascript\nimport React from 'react'\nimport Rx from 'rx'\nimport { compose, withElmish } from '../../lib'\n\n// Render view\nexport const Interval = props =\u003e (\n  \u003cdiv className=\"widget\"\u003e\n    Component interval counter via functional composition: {props.model.count}\n  \u003c/div\u003e\n)\n\nconst elmishInterval = withElmish({\n  // Prepare initial state\n  init () {\n    return {\n      model: {\n        count: 0\n      },\n      // command to subscribe to side-effects (optional)\n      cmd: 'INTERVAL_START'\n    }\n  },\n  // Update state\n  update (model, message) {\n    model.count += 1\n    return { model }\n  },\n  // Attach subscriptions to external side-effects\n  subscriptions (cmd, $observable) {\n    switch (cmd) {\n      case 'INTERVAL_START':\n        return Rx.Observable.interval(1000).map(x =\u003e 'INTERVAL_ELAPSED')\n    }\n  }\n})\n\nexport default compose(elmishInterval)(Interval)\n```\n\n### Use Reactish-Elmish with class extention\n\n```javascript\nimport React from 'react'\nimport { ReactComponent } from '../../lib'\n\nexport default class MyComponent extends ReactComponent {\n  // Prepare initial state\n  init() {\n    return {\n      model: { something: 'init', }\n    }\n  }\n\n  // Update state\n  update(model, message) {\n    switch (message.type) {\n      case 'DO_SOMETHING':\n        return {\n          model: { something: 'Foo Bar', }\n        }\n      default:\n        return {\n          model\n        }\n    }\n  }\n\n  // Render view\n  view(model, action) {\n    return (\n      \u003cdiv\u003e\n        {JSON.stringify(model, null, 4)}\n        \u003cbutton onClick={() =\u003e action({type: 'DO_SOMETHING', model})}\u003eClick\u003c/button\u003e\n      \u003c/div\u003e\n    )\n  }\n\n}\n```\n\n## Testing on Component-Level\n\n```bash\nnpm test\n```\n\n* [Delayed Counter Example](tests/DelayedCounter.test.js)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikebild%2Freactish-elmish","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmikebild%2Freactish-elmish","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikebild%2Freactish-elmish/lists"}