Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/zambezi/caballo-vivo
Glue code for pure RxJS applications to connect with React-Router, and other operators
https://github.com/zambezi/caballo-vivo
Last synced: 12 days ago
JSON representation
Glue code for pure RxJS applications to connect with React-Router, and other operators
- Host: GitHub
- URL: https://github.com/zambezi/caballo-vivo
- Owner: zambezi
- Created: 2018-11-15T15:48:03.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-05-02T14:30:54.000Z (over 1 year ago)
- Last Synced: 2024-12-08T08:44:24.024Z (19 days ago)
- Language: JavaScript
- Homepage:
- Size: 253 KB
- Stars: 3
- Watchers: 7
- Forks: 7
- Open Issues: 15
-
Metadata Files:
- Readme: README.adoc
Awesome Lists containing this project
README
= Caballo Vivo
[quote, CJC]
_1177._ los cementerios no deben estrenarse enterrando un hombre muerto sino un caballo vivoThin, opinionated layer for pure RxJS applications to connect with React-Router (or any router using `history`). It has the advantage of scaling well, allowing more concise and expressive code and a better separation between business logic and view layer.
Here's the equivalent implementation of the Redux Reddit advanced tutorial with caballo-vivo, in less than 100 lines of code (https://codesandbox.io/s/caballo-vivo-reddit-w88kg[online sandbox]):
[source,javascript]
----
// type localStorage.setItem('cv-log', true) in the console to see the logs!
import React from "react"
import { render } from "react-dom"
import { OrderedMap, Map } from "immutable"
import { partialRight } from "ramda"
import { Subject, merge, concat, of, from } from "rxjs"
import { map, catchError, switchMap } from "rxjs/operators"
import { Router, Switch, Route, Link } from "react-router-dom"
import {
createLocation$,
createNavigateTo$,
createStore$,
history,
flog,
stow
} from "@zambezi/caballo-vivo"
import ClipLoader from "react-spinners/ClipLoader"
import "./index.css"const getPosts$ = new Subject()
const pathToIntent = OrderedMap([
["/:subreddit", ({ subreddit }) => getPosts$.next({ subreddit })],
["/", () => getPosts$.next({ subreddit: "all" })]
])const location$ = createLocation$(pathToIntent).pipe( // <1>
map(location => state => state.set("location", location))
)const redditJourney$ = getPosts$.pipe( // <2>
switchMap(({ subreddit }) =>
concat(
of(state => state.set("loading", true)), // <3>
from(fetch(`https://www.reddit.com/r/${subreddit}.json`)).pipe(
switchMap(res =>
from(res.json()).pipe(
map(json => json.data.children.map(child => child.data))
)
),
stow("subreddit") // <4>
),
createNavigateTo$(subreddit), // <5>
of(state => state.set("loading", false))
)
),
catchError(() =>
of(state => state.set("error", "This subreddit is not available"))
)
)merge(location$, redditJourney$)
.pipe(createStore$(Map()), flog("Render state"), map(toView)) // <6>
.subscribe(partialRight(render, [document.getElementById("root")]))function toView(state) { // <7>
if (state.has("error")) return{state.get("error")}
if (state.get("loading"))
return (
)
return (
{["all", "reactjs", "rxjs", "javascript", "node"].map(l => (
{`r/${l}`}
))}
-
{post.title}
{state.get("subreddit").map(post => (
))}
)
}
----
<1> Passing a map to `createLocation$` will block the hisory when the route changes and the new url matches one of the keys.
<2> We listen to the `getPosts$` intent, triggered by the routes above, to kickstart a complex series of asynchronous events (set/unset loader flag, download subreddit).
<3> When we emit reducer functions, they get executed by the store (see below). All that a reducer function does is get the current state snapshot and return a new snapshot.
<4> https://github.com/zambezi/caballo-vivo/blob/master/src/stow.js[stow] is just an operator which emits a reducer function to `set` the value received on a key or path in the store. This pattern is so common that it got its own operator.
<5> `createNavigateTo$` unblocks the history and changes the address in the URL bar. At this point, all the routes matching the new address can render.
<6> `createStore$` creates a store, which is an rxjs operator that has an initial value, takes reducers function in and spits state snapshots out.
<7> The view function is just a function which takes state snapshots in and generates JSX. Views and state / business logic are decoupled and interchangeable.
For a more advanced example application, please check: https://github.com/gabrielmontagne/fa-doodle