Skip to content

Instantly share code, notes, and snippets.

@milankinen

milankinen/app.js

Created Jan 25, 2016
Embed
What would you like to do?
fluorine+combinators
import React from "react"
import {Subject, Observable} from "rx"
import {render} from "react-dom"
import {Combinator} from "react-combinators/rx"
import {createDispatcher} from "fluorine-lib"
const initialBMI = {weight: 80, height: 180}
function BMI(state, action) {
state = state || initialBMI
return action.type === "SET" ? {...state, [action.key]: action.value} : state
}
function Model() {
const dispatcher = createDispatcher()
const model = dispatcher.reduce(BMI).shareReplay()
const weight = model.map(m => m.weight).share()
const height = model.map(m => m.height).share()
const bmi = Observable
.combineLatest(weight, height)
.map(([w, h]) => Math.round(w / (h * h * 0.0001)))
.share()
return {
weight, height, bmi,
setWeight: value => dispatcher.dispatch({type: "SET", key: "weight", value}),
setHeight: value => dispatcher.dispatch({type: "SET", key: "height", value})
}
}
function App() {
// and here we can use the same object like any other object
const { setHeight, setWeight, height, weight, bmi } = Model()
return (
<Combinator>
<div>
<h1>BMI counter example</h1>
{renderSlider("Height", height, setHeight, 100, 240)}
{renderSlider("Weight", weight, setWeight, 40, 150)}
{/* and here we can embed the observables directly into the JSX */}
Your BMI is: <span className="bmi">{bmi}</span>
</div>
</Combinator>
)
}
function renderSlider(title, value, setValue, min, max) {
return (
<div>
{title}: {value} <br />
<input type="range" min={min} max={max} value={value} className={title}
onChange={e => setValue(e.target.value)} />
</div>
)
}
render(<App />, document.getElementById("app"))
@kitten

This comment has been minimized.

Copy link

@kitten kitten commented Jan 25, 2016

This looks pretty nice. :) I'm wondering how a complete example would look. I'll probably build something like that soon to see whether it is a nice replacement for decorators and top-level components.

How's the Combinator's performance? Is it worse than top level components? Does it traverse the virtual dom every time an observable emits a new value?

@kitten

This comment has been minimized.

Copy link

@kitten kitten commented Jan 25, 2016

@milankinen I've just tried the counter example that I've got on the fluorine repo with a combinator. Simple enough I've just dropped it at the top level, which didn't work, but I'll have to investigate that. Dropping it a level deeper worked and there's no notable performance difference. (Maybe it was just the derp though? :D)

This is a very simple example and I have to check how it holds up against bigger codebases. But dropping the observables directly into the jsx is very freeing, as the withStore decorator can be removed completely.

Then again the decorator saves the code from doing another breadth search of all nodes - even though that's an implementation detail and probably not inefficient.

But at the end of the day the difference in the example would comes down to usage, which looks probably like this in the end:

@withStore(dispatcher.reduce(reducer).map(x => x.get('value')), 'value')
class Component {
  render() {
    return (
      <div>
        {this.props.value}
      </div>
    )
  }
}

vs

const value = dispatcher.reduce(reducer).map(x => x.get('value'))

class Component {
  render() {
    return (
      <div>
        {value}
      </div>
    )
  }
}

The difference is probably crucial when you eliminate as much component state as possible and just use pure functions as components, which is probably a nice use case as well, but which unfortunately can also be solved by using a decorator:

let component = ({value}) => (
  <div>
    {value}
  </div>
)

component = withStore(dispatcher.reduce(reducer).map(x => x.get('value')), 'value')(component)

export default component

vs

const value = dispatcher.reduce(reducer).map(x => x.get('value'))

export default const component = () => (
  <div>
    {value}
  </div>
)

This is now a clear improvement, but still I'm not sure if it's worth the price ^^

I'd love to hear more comments from you on this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.