Skip to content

Instantly share code, notes, and snippets.

@dmitriid
Last active April 28, 2016 13:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmitriid/23c080ce5acf527ebb6fe89e0b55928b to your computer and use it in GitHub Desktop.
Save dmitriid/23c080ce5acf527ebb6fe89e0b55928b to your computer and use it in GitHub Desktop.

The problem concerns the following: How to properly isolate components in lists, and work with events from those components?

Problem description:

  • retrieve a list of objects (called events) from the server
  • create and display a list of these events
  • each event display has a "Sign up" button. When clicking this button we should produce a reaction to the corresponding event

Given the code below:

  • The HTTP request is for some reason performed twice (should happen only once, on startup).
// app.js

import event from '../event/event'

const renderEvent = (sources, model$) => {
    const eventActionProxy$ = new Rx.Subject()

    const DOM$ = model$.map((evts) => {
      return evts.map((evt) => {
          const e = isolate(event)({DOM, evt, eventActionProxy$})
          return e.DOM
        })
    })

    return {
      DOM: DOM$,
      signup$: eventActionProxy$
    }
}

const model = ({HTTP}) => {
  return HTTP.map((res) => res.body.data.events)
             .startWith([])
}

export default (sources) => {
  const model$ = model(sources)

  const events = renderEvents(sources, model$)

  const signUp$ = events.signup$
                        .do((x) => console.log('signup: ', x))
                        .map(() => div('SIGNUP CLICKED!!'))


  return {
    DOM: events.DOM.combineLatest(signUp$.startWith(''), (x, y) => [x, y]).map((x) => div(x)),
    HTTP: /* ... generate the proper HTTP request ...*/
  }
}

And the event:

const intent = (DOM, evt, actionProxy$) => {
  const click$ = DOM.select('.event-signup')
                    .events('click')
                    .do((e) => e.preventDefault())
                    .do((x) => console.log('clicked!!!'))

  click$.subscribe(() => actionProxy$.onNext({
    type: 'signup',
    data: evt
  }))

  return click$
}

const view = (evt) => {
  return Rx.Observable.just(
    div([
      h1(evt.name),
      div(evt.atmosphere),
      div(evt.music),
      div(evt.description),
      div('.event-signup', 'Sign up')
    ])
  )
}

export default ({DOM, evt, eventActionProxy$}) => {
  intent(DOM, evt, eventActionProxy$)
  return {
    DOM: view(evt)
  }
}
@chadrien
Copy link

My view on this code

const DOM$ = model$.map((evts) => {
  return evts.map((evt, i) => {
    const e = isolate(event, `event${i}`)({DOM, evt, eventActionProxy$})
    return e.DOM
  })
})
  • by using the event component's sink I think you could avoid using a proxy (which seems "dirty" to me)
// event.js
const intent = (DOM, evt) => {
  return Observable.of({type: 'signup', data: evt})
    .sample(DOM.select('.event-signup').events('click'))
    .repeat()
}

export default ({DOM, evt}) => {
  return {
    DOM: view(evt),
    click$: intent(DOM, evt)
  }
}


// app.js
const renderEvents = ({DOM}, model$) => {
  const events$ = //…

  return {
    DOM: events$.map(events => events.map(event => event.DOM)),
    signup$: events$
      .map(evts => evts.reduce((clicks$, evt) => clicks$.merge(evt.click$), Observable.empty()))
      .switch()
  }
}

What this code do is that it creates a single stream of clicks$ out of the array of event (that's the merge in the reduce) and then we use switch as we end up with a stream of stream, and switch "flattens" a stream of stream.

No more proxy and I think we are closer to a fractal architecture than with a proxy.

Webpackbin of this code: http://www.webpackbin.com/41CU7lie-

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment