Search code examples
javascriptfunctional-programmingcyclejs

Cycle.js - How to get collection length in a collection item


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?


Solution

  • 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 })