Cycle.js HTTP response prints as undefined in DOM

82 Views Asked by At

I am trying to print the results of an HTTP request to the DOM in Cycle.js. Right now I am just trying to list the results but they will eventually be in a select dropdown.

I create the stream...

const facilityRequest$ = xs.of({
  url: 'http://...',
  category: 'facility'
});

Then select it...

 const facilityResponse$ = sources.HTTP
  .select('facility')
  .flatten()
  .map(res => res.body.data)
  .startWith([]);

But when I try to add the results to the top like:

const vdom$ = form$.map(form =>
  div([
    facilityResponse$.map(res => p(res.id)),
  ])
);

It displays as undefined in the browser window.

However if I add a listener and log the response to the console it displays the array of facilities:

facilityResponse$.addListener({
  next: (res) => {
    console.log(res);
  }
})
2

There are 2 best solutions below

0
On

It turns out that lists are notoriously difficult to handle with cycle.js. To do it you can either use the @cycle/collection library or map over the array elements and combine them into a single item (it depends on what you are wanting to do with it).

I solved my particular case with:

const facilityVDom$ = facilityResponse$.map(facilities => ul(facilities.map(f => li(f.id))));

Here are some relevant discussions:

1
On

Yes! You found the correct way to handle your stream of arrays.

// CORRECT :)
const facilityVDom$ = facilityResponse$.map(facilities => // <- this is an array
  ul( // <- notice no brackets here, because you already have an array
    facilities.map(f => li(f.id)) // <- ...and array map returns an array
  )
);

However this is not a difficulty with lists problem.

STREAMS are like arrays over time, and it's useful to think of them as arrays, but they are not ARRAYS and are not interchangeable with arrays. Only use streams in STREAM context and only use arrays in ARRAY context—hence the problem with your first attempt:

// WRONG :(
const vdom$ = form$.map(form =>
  div([ // <-- the `div` function can take an ARRAY of VDOMs as parameter
    facilityResponse$.map(res => p(res.id)), // <-- this is a STREAM, not a VDOM
  ])
);

You have two mistakes here: 1) you used a stream in an array context, and 2) you wrapped what you thought was interchangeable with an array in an array. So the parameter you provided to the div function was an array with one element and that element was a stream, not a VDOM object.


The ARRAY provided to the div (or similar) function must contain VDOM objects (or functions that return VDOM objects). Such as:

div( [ p('1'), p('2'), p('3') ] ) // OR

div( [1,2,3].map(ii => p(ii))   ) // OR

let arr = [1,2,3]
div( arr.map(ii => p(ii))       )

The map method on a STREAM takes a function and that function switches you out of stream context. You can actually use streams within this function but they're are not typically useful and are never interchangeable with arrays.

stream$.map(         // <- stream
  function (elem) {  // <- leaving stream

    // regular javascript here
    // transform each element of the stream
    // and return it to the stream
    return elem
  }
)                    // <- back to stream