Created
October 10, 2017 19:06
-
-
Save olpeh/3edb1ab8bc93a7442559a6efb988c5bb to your computer and use it in GitHub Desktop.
Cycle.js state management example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import xs from "xstream"; | |
import { run } from "@cycle/run"; | |
import { | |
makeDOMDriver, | |
h1, | |
h4, | |
div, | |
select, | |
option, | |
span, | |
input | |
} from "@cycle/dom"; | |
import onionify from "cycle-onionify"; | |
function renderOption(currency, selectedCurrency) { | |
return currency === selectedCurrency | |
? option( | |
{ | |
attrs: { | |
selected: "selected" | |
} | |
}, | |
[currency] | |
) | |
: option([currency]); | |
} | |
function intent(DOMSource) { | |
const currencyChangeAction$ = DOMSource.select(".currency") | |
.events("input") | |
.map(ev => ({ | |
type: "CURRENCY_CHANGE", | |
payload: ev.target.value | |
})); | |
const priceInputAction$ = DOMSource.select(".price") | |
.events("input").debug() | |
.map(ev => ({ | |
type: "PRICE_INPUT", | |
payload: ev.target.value | |
})); | |
return xs.merge(currencyChangeAction$, priceInputAction$); | |
} | |
function model(action$) { | |
const initReducer$ = xs.of(function initReducer(prevState) { | |
// Note that we ignore the prevState argument given, | |
// since it's probably undefined anyway | |
return { currency: "€", price: 100 }; // this is the initial state | |
}); | |
const currencyChangeReducer$ = action$ | |
.filter(ac => ac.type === "CURRENCY_CHANGE") | |
.map( | |
ac => | |
function currencyChangeReducer(prevState) { | |
return { | |
...prevState, | |
currency: ac.payload | |
}; | |
} | |
); | |
const priceInputReducer$ = action$ | |
.filter(ac => ac.type === "PRICE_INPUT") | |
.map( | |
ac => | |
function priceInputReducer(prevState) { | |
return { | |
...prevState, | |
price: ac.payload | |
}; | |
} | |
); | |
return xs.merge(initReducer$, currencyChangeReducer$, priceInputReducer$); | |
} | |
function view(state$) { | |
return state$.map(state => | |
div([ | |
h1("Cycle.js simple example"), | |
span("Select currency"), | |
select(".currency", [ | |
renderOption("€", state.currency), | |
renderOption("$", state.currency), | |
renderOption("¥", state.currency) | |
]), | |
div([ | |
span("Set a price"), | |
input(".price", { | |
attrs: { | |
type: "number", | |
min: 0, | |
value: state.price | |
} | |
}) | |
]), | |
div(".result", [ | |
h4(`Now you have selected: ${state.price}${state.currency}`) | |
]) | |
]) | |
); | |
} | |
function main(sources) { | |
const state$ = sources.onion.state$; | |
const action$ = intent(sources.DOM); | |
const reducer$ = model(action$); | |
const vdom$ = view(state$); | |
const sinks = { | |
DOM: vdom$, | |
onion: reducer$ | |
}; | |
return sinks; | |
} | |
const drivers = { | |
DOM: makeDOMDriver("#app") | |
}; | |
const wrappedMain = onionify(main); | |
run(wrappedMain, drivers); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment