BeniaminoBaggins
BeniaminoBaggins

Reputation: 12443

Conditional rendering in cycle.js with RxJS

I have a pretty simple conditional rendering scenario where if props.showCalc is true, I will render the calculator, but if false, I will render the title:

function view(sources) {
  const props$ = sources.props
  const titleVDom$ = Title(sources).DOM
  const calcVDom$ = Calculator(sources).DOM
  const vdom$ = props$
    .map((props) =>
      <section className="hero is-fullheight is-primary">
        <div className="hero-head">
        </div>
        <div className="hero-body">
          {props.showCalc ? {calcVDom$} : {titleVDom$}}
        </div>
        <div className="hero-foot">
        </div>
      </section>
    )
  return vdom$
}

This doesn't work, because you can't pass a stream DOM to JSX to render. I've always had to map the stream DOM in order to render it.

How could I render the {calcVDom$} or {titleVDom$} in this scenario? As is, they both render "undefined" because they are streams.

I tried to map calcVDom$/titleVDom$ halfway down the render function but it was becoming really messy.

Upvotes: 3

Views: 202

Answers (1)

CitizenNito
CitizenNito

Reputation: 326

You could chain the streams and merge the results before mapping them to the JSX? Something like

function view(sources) {

  const props$ = sources.props;
  const titleVDom$ = Title(sources).DOM;
  const calcVDom$ = Calculator(sources).DOM;

  const vdom$ = props$
    .mergeMap((props) => titleVDom$.map((title) => [ props, title ]))
    .mergeMap((data) => calcVDom$.map((calc) => data.push(calc)))
    .map((data) =>
      <section className="hero is-fullheight is-primary">
      <div className="hero-head">
      </div>
      <div className="hero-body">
        {data[0].showCalc ? {data[2]} : {data[1]}}
      </div>
      <div className="hero-foot">
      </div>
    </section>
  );

  return vdom$;
}

You could also achieve the same thing using forkJoin:

function view(sources) {

  const props$ = sources.props;
  const titleVDom$ = Title(sources).DOM;
  const calcVDom$ = Calculator(sources).DOM;

  return Rx.Observable.forkJoin([
    props$,
    titleVDom$,
    calcVDom$
  ]).map((data) =>
      <section className="hero is-fullheight is-primary">
      <div className="hero-head">
      </div>
      <div className="hero-body">
        {data[0].showCalc ? {data[2]} : {data[1]}}
      </div>
      <div className="hero-foot">
      </div>
    </section>
  );
}

The difference being that forkJoin will subscribe to all of the Observables (triggering any action) simultaneously while the mergeMap method will chain them, only triggering the second and third when (and if) the preceding ones have emitted a value.

Upvotes: 3

Related Questions