2

I'm trying to add some behavior exclusively to the last item in a list in Cycle.js. I tried to use cycle-onionify to make a collection like so:

const List = makeCollection({
  item: Child,
  itemKey: (childState, index) => String(index),
  itemScope: key => key,
  collectSinks: instances => {
    return {
      onion: instances.pickMerge('onion'),
      DOM: instances.pickCombine('DOM')
        .map(itemVNodes => ul(itemVNodes))
    }
  }
});

I understand that lenses can be used to share state between components, but there doesn't seem to be a way to use lenses with a collection. I'm thinking I could pass the Collection length to the children so I could compare it with an id.

Is there something I am missing?

sliptype
  • 2,804
  • 1
  • 15
  • 26

1 Answers1

2

You can use lenses with makeCollection. Remember it returns a normal Cycle.js component that you can isolate. So if you want to add a boolean isLast you can do this like this:

function omit(obj, key) {
    let tmp = { ...obj }; //Copy the object first
    delete tmp[key];
    return tmp;
}

const listLens = {
   get: stateArray => stateArray.slice(0, -1).concat({
            ...stateArray[stateArray.length - 1],
            isLast: true
        }),
   set: (stateArray, mappedArray) => mappedArray.slice(0, -1)
           .concat(omit(mappedArray[mappedArray.length - 1], 'isLast'))
};

const List = isolate(
    makeCollection({
        item: Child,
        itemKey: (childState, index) => String(index),
        itemScope: key => key,
        collectSinks: instances => ({
            onion: instances.pickMerge('onion'),
            DOM: instances.pickCombine('DOM')
                .map(itemVNodes => ul(itemVNodes))
        })
    }),
    { onion: listLens, '*': null }
);

As a side note, if you want to apply a lens on each individual item, you can do so too, with the itemScope property. For example

itemScope: key => ({ onion: myLens, '*': key })
Jan van Brügge
  • 742
  • 8
  • 13