Skip to content

Instantly share code, notes, and snippets.

@milankinen
Last active November 4, 2015 22:34
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save milankinen/465535f18bd381ff874f to your computer and use it in GitHub Desktop.
atomiperffiä
import React from "react"
import Bacon from "baconjs"
import {render} from "react-dom"
import {range, sum} from "lodash"
import combineAsComponent from "./combineAsComponent"
import Atom from "./atom"
function initApp() {
const bmiModels = Atom([BMIModel()])
const bmiComponents =
bmiModels.map(models => models.map(m => <BMI model={m} />))
// just for fun
bmiModels
.flatMapLatest(models => Bacon.combineAsArray(models.map(m => m.bmiP)))
.map(bmis => sum(bmis) / bmis.length)
.log("avg bmi")
return Bacon.combineTemplate(
<div>
<h1>BMI counterz ({bmiModels.map(".length")})</h1>
<hr />
<button onClick={() => bmiModels.swap(models => [...models, ...range(1000).map(_ => BMIModel())])}>
Add
</button>
<div>{bmiComponents}</div>
</div>
)
}
const BMIModel = () => {
const weight = Atom(70)
const height = Atom(170)
const bmiP = Bacon.combineWith(weight, height, (w, h) => Math.round(w/(h * h * 0.0001)))
return {weight, height, bmiP}
}
const bmi = ({weight, height, bmiP}) =>
Bacon.combineTemplate(
<div>
<hr />
{slider("Weight", "kg", 40, 140, weight)}
{slider("Height", "cm", 140, 210, height)}
BMI: {bmiP}
</div>
)
const BMI = combineAsComponent(({model}) => model.flatMapLatest(bmi))
const slider = (title, units, min, max, value) =>
Bacon.combineTemplate(
<div>
{title}: {value} {units}
<div>
<input type="range" min={min} max={max} value={value}
onChange={e => value.reset(e.target.value)}/>
</div>
</div>
)
initApp().onValue(app => render(app, document.getElementById("todoapp")))
import Bacon from "baconjs"
export default function Atom(initialValue) {
const bus = new Bacon.Bus()
const atom = bus.scan(initialValue, (state, fn) => fn(state))
atom.reset = val => bus.push(_ => val)
atom.swap = fn => bus.push(fn)
return atom
}
import Bacon from "baconjs"
import React from "react"
import {keys, values, zip, zipObject} from "lodash"
/**
* Wraps function (...args) => Observable into React.Component so that
* properties that are passed from parent elements are transformed to EventStreams
* (e.g. (prop1, prop2) => (Observable(prop1), Observable(prop2))
*
* Example:
*
* const App = React.createClass({
* render() {
* const {width, height} = this.state // change this somehow
* return (
* <div>
* Example
* <BMI width={width} height={height} />
* </div>
* )
* }
* })
*
* const BMI = combineAsComponent({width, height} => {
* const bmiP = Bacon.combineWith(weight, height, (w, h) => Math.round(w/(h * h * 0.0001)))
* return Bacon.combineTemplate(
* <div>BMI for {width} and {height} is {bmiP}</div>
* )
* })
*/
export default function combineAsComponent(renderFn) {
return React.createClass({
getInitialState() {
const propsBuses = zipObject(keys(this.props), values(this.props).map(val => new Bacon.Bus()))
const propsS =
zipObject(keys(this.props), zip(values(propsBuses), values(this.props)).map(([bus, initial]) => (
bus.startWith(initial).skipDuplicates()
)))
return {
propsBuses,
vdomS: renderFn(propsS),
vdom: null
}
},
componentWillMount() {
const updateVDOM = vdom => this.setState({vdom})
if (process.browser) {
this.setState({ dispose: this.state.vdomS.onValue(updateVDOM) })
} else {
this.state.vdomS.take(1).onValue(updateVDOM)
}
},
componentWillReceiveProps(nextProps) {
keys(nextProps).forEach(propName => {
const bus = this.state.propsBuses[propName]
if (!bus) {
console.warn(
`Trying to pass property "${propName}" that is not set during the component creation.`,
`Ignoring this property.`
)
} else {
bus.push(nextProps[propName])
}
})
},
shouldComponentUpdate(nextProps, nextState) {
return nextState.vdom !== this.state.vdom
},
componentWillUnmount() {
const {dispose} = this.state
if (dispose) {
dispose()
}
},
render() {
return this.state.vdom
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment