Animating "item removed from list" updates : how to delay doc removal on DDP 'removed' message?

My sample app case:
(simplified version of the actual app I’m dealing with) :

An app displays lists of items :

  • The Lists collection contain List documents,
    each List has an array of itemIds (_ids of Item documents)

  • The Items collection contains Item documents
    each Item has a title,

A publication exposes ItemsInList(listId), using publish-composite - straighforward implementation:

Meteor.publishComposite('itemsInList', function (listId) {
return {
  find: () => Lists.find(listId),
  children: [
    {
      find: (List) => {
        let ids = List.itemIds;
        return ids.length && Items.find({ _id: { $in: ids } });
      }
    }
  ]
};

There are methods to add/remove/reorder an item in a list

The client code animates changes in the displayed list (using the awesome [react-flip-move package](https://github.com/joshwcomeau/react-flip-move in my case, but could be any other lib that adds onLeave animations), and notably the disappearance of an item :
When an item is removed from a list, the displayed item fades out, and the rest of the items move up

My issue is this :

When a user removes an item from a list, the removeItem method updates the List.itemIds to remove the itemId
The publish-composite publication immediately sends :

  • A DDP ‘changed’ message for the list. On clients, this reactively triggers a re-render of the List, with the item being removed and thus being animated out
  • And then a DDP ‘removed’ message for the item. On clients, the Item is removed from minimongo straight away, and depending on the implementation of “is loading” logic, this displays a loader or just nothing

–> Depending on network lag, you see the item start to fade out, and then suddenly disapper in the middle of the animation when the DDP ‘removed’ arrives.

Is there a way to keep ‘removed’ documents around in minimongo for some time, while their data is still needed to display their UI representation being animated out ?

I think what is needed is not that they remain in the Collection, but that the data doesn’t get removed from the DOM. I think this is what percolate:momentum does

@herteby:

I think what is needed is not that they remain in the Collection, but that the data doesn’t get removed from the DOM. I think this is what percolate:momentum does

Hm… that seems far from trivial though. The typical flow in a Meteor/React UI is
"get the List doc from minimongo in react-meteor-data’s getContainer HOC, then display it".

Maintaining an intermediate buffer between “what is in the store (minimongo)” and “what gets displayed in the DOM” sounds like a hassle.

Haven’t looked at percolate:momentum yetn but from the last commit date (2 years ago) and the disclaimer in the README, it doesn’t seem to have gone past the experimental status.

FWIW, hacking with the following code does produce the intended effect :

 if (Meteor.isClient) {
  let origRemove = Items._collection.remove.bind(Items._collection);
  Items._collection.remove = function (...args) {
    Meteor.setTimeout(() => origRemove(...args), 1000);
  };
}

The drawback is that it breaks the API / return value of Collection.remove(), so that doesn’t really feel like a viable solution

Ah, yeah I don’t know about the best approach for React. Momentum is for Blaze anyway. Is there some function in React to freeze reactivity for a component maybe?

I’m using Vue, which has this functionality already built into its transition feature.

But I think since this is just a display feature, it should be handled by the render function and not affect the actual Collection.

I guess delaying the actual doc removal on DDP ‘removed’ message would also introduce its share of tricky concurrency cases to handle (like a DDP ‘removed’ immediately followed by a DDP ‘added’ on the same doc due to regular subscription updates - then the delayed ‘remove’ would end up removing the doc that was rightfully added)

Freezeing React component updates in the case “this prop had a value and now it’s empty” indeed seems more like the right approach.

Thanks for the feedback :slight_smile: