https://github.com/mikebild/reactish-elmish
Proof of Concept - RxJS + React -> Elmish
https://github.com/mikebild/reactish-elmish
component design elm proof-of-concept react rxjs
Last synced: over 1 year ago
JSON representation
Proof of Concept - RxJS + React -> Elmish
- Host: GitHub
- URL: https://github.com/mikebild/reactish-elmish
- Owner: MikeBild
- Created: 2016-10-03T20:34:50.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2017-06-07T06:27:00.000Z (about 9 years ago)
- Last Synced: 2025-02-28T18:42:49.278Z (over 1 year ago)
- Topics: component, design, elm, proof-of-concept, react, rxjs
- Language: JavaScript
- Homepage: https://reactish-elmish.cloud.dropstack.run
- Size: 209 KB
- Stars: 13
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# React + RxJS -> Elmish
> __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!
## [Live Demo](https://reactish-elmish.cloud.dropstack.run)
## Setup
```bash
npm install
npm test
npm run dev
```
## Build
```bash
npm run build
```
## Deploy to [https://dropstack.run](https://dropstack.run)
```bash
npm run deploy
```
## Flow

## Introduction
* Frontend-Developer friendly syntax and semantic (similar to Event/Game-Loop)
* Fits for "smart" components (side effects + state management + logic + rendering)
* Stateless React Functions for "dumb" components
* Component based state and side effect management (avoid central state)
* Minimal inversive - combine React-Rendering and Reactive Extentions without building a Framework with unnecessary abstractions
* Follow the principle of "reactive" fold left with [Rx Scan Operator](http://rxmarbles.com/#scan)
* Async/Sync side effect orchestration via [Rx-Observables](http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html)
## Reative Extentions vs. Elm Side-Effects
* Subscriptions in Elm signals **"only" next message**. No publishing of error and complete signals. Just use a **"message content filter"** in model update functions.
* To handle and chain asynchronous side-effects Elm have the concept of [Tasks](http://package.elm-lang.org/packages/elm-lang/core/latest/Task)
* Tasks have success and error signals
* You can chain your side-effects with `task.andThen`
## Handle side-effects and compose asynchron data flows
* Reactish-Elmish manage side-effects using observable from the `subscription(...)` function
* Observables are streams of messages
* Observables are decomposable to smaller observables
* Observables are composable to larger observables
* Every observable has three different kinds of "signals" to manage the lifecycle (subscription state) of the stream
* next - data message pushed to the stream
* error - error message pushed to the stream (end of subscription)
* complete - done message pushed to the stream (end of subscription)
### Creation of initial stream
* Return an observable (e.g. Rx.Observable.interval(...) in your component using the `subscription(cmd, $observable)` function
* You don't need to `subscribe()` inside of your `subscription(...)` function. Just, send a command and Reactish-Elmish will `subscribe(...)` to your created observable.
* Reactish-Elmish calls `update(message, ...)` via `$observable.do(...)` for every message pushed from the created observable
### Handle the stream lifecycle
* You get your created observable reference to the second function argument [**(message, $observable) -> $observable)**]
* Decompose to smaller data flows via observable operators like (.filter(...), .take(...), etc.)
* Compose to larger data flows via observable operators like (.merge(...), .concat(...), etc.)
* Compose with other observables to manage the subscription lifecycle using (.takeUntil(...), .takeWhile(...), etc.)
## Examples
* [All in One](http://reactish-elmish.cloud.dropstack.run/allinone)
* [Counter](http://reactish-elmish.cloud.dropstack.run/counter)
* [Counter delayed](http://reactish-elmish.cloud.dropstack.run/counterdelayed)
* [Counter composed](http://reactish-elmish.cloud.dropstack.run/countercomposed)
* [Form / Validation](http://reactish-elmish.cloud.dropstack.run/form)
* [Interval component](http://reactish-elmish.cloud.dropstack.run/intervalcomponent)
* [Interval component via functional composition](http://reactish-elmish.cloud.dropstack.run/intervalcomponentcompose)
* [Random Gif (Fetch)](http://reactish-elmish.cloud.dropstack.run/randomgiffetch)
* [Random Gif (Rx)](http://reactish-elmish.cloud.dropstack.run/randomgifrx)
* [Optimistic UI updates](http://reactish-elmish.cloud.dropstack.run/optimisticupdate)
* [Long-Running Process (aka Saga) (RxJs)](http://reactish-elmish.cloud.dropstack.run/ordersaga)
* [CouchDB/PouchDB change feed via AJAX long polling](http://reactish-elmish.cloud.dropstack.run/webevents)
* [EventSource via HTTP/2 push notifications](http://reactish-elmish.cloud.dropstack.run/http2eventsource)
* [Inter-component communication via global/parent state handling (aka createStore/reducer)](http://reactish-elmish.cloud.dropstack.run/componentcommunication)
* [Inter-component communication via streams (RxJs)](http://reactish-elmish.cloud.dropstack.run/componentcommunicationviastream)
* [Component composition with GraphQL data fetching (Apollo-Client)](http://reactish-elmish.cloud.dropstack.run/componentwithgraphql)
## Elmish API
**init(props)** function, returning the initial model (a state is an object with a required key model and an optional key cmd) [**() -> model**]
**update(model, message)** reduce function, returns the new model [**(model, message) -> model**]
**view(model, action)** function, returns the updated UI (React) [**(model) -> html**]
**subscription(cmd, $observable)** function, manage composable side effects returns an Rx observable (stream of messages) [**(message, $observable) -> $observable)**]
## Reactish API
### Use Reactish-Elmish with functional composition (compose / withElmish)
```javascript
import React from 'react'
import Rx from 'rx'
import { compose, withElmish } from '../../lib'
// Render view
export const Interval = props => (
Component interval counter via functional composition: {props.model.count}
)
const elmishInterval = withElmish({
// Prepare initial state
init () {
return {
model: {
count: 0
},
// command to subscribe to side-effects (optional)
cmd: 'INTERVAL_START'
}
},
// Update state
update (model, message) {
model.count += 1
return { model }
},
// Attach subscriptions to external side-effects
subscriptions (cmd, $observable) {
switch (cmd) {
case 'INTERVAL_START':
return Rx.Observable.interval(1000).map(x => 'INTERVAL_ELAPSED')
}
}
})
export default compose(elmishInterval)(Interval)
```
### Use Reactish-Elmish with class extention
```javascript
import React from 'react'
import { ReactComponent } from '../../lib'
export default class MyComponent extends ReactComponent {
// Prepare initial state
init() {
return {
model: { something: 'init', }
}
}
// Update state
update(model, message) {
switch (message.type) {
case 'DO_SOMETHING':
return {
model: { something: 'Foo Bar', }
}
default:
return {
model
}
}
}
// Render view
view(model, action) {
return (
{JSON.stringify(model, null, 4)}
action({type: 'DO_SOMETHING', model})}>Click
)
}
}
```
## Testing on Component-Level
```bash
npm test
```
* [Delayed Counter Example](tests/DelayedCounter.test.js)