Animate entry of (only new) elements in a virtualized list

913 Views Asked by At

I am using react-window FixedSizedList and react-virtualized-auto-sizer Autosizer components to build a list/table UI element that may contain thousands of items that also receives new items via a websocket connection and prepends them to the list. I now have a requirement to animate the entry of new elements in this list.

Here is a codesandbox link with a minimal (and not quite working) example: codesandbox.

Note how the .row animation is triggered for every child of FixedSizedList each time the data list receives a new item. Also note how the .row animation is again triggered for every child of FixedSizedList when the list is scrolled.

I understand that the reason this is happening is because of how list virtualization works using absolute positioning of the children rows. Every time a new item is inserted into data, or the list is scrolled, react-window needs to re-compute the style prop for each row, which recreates every DOM element and hence re-triggers the .row animation.

I would like to animate only the new .row DOM elements when they appear.

Things I have tried:

  1. Add animation-iteration-count: 1; to the .row class. This doesn't work for the same reason mentioned above as every element is re-created on a new item insert.
  2. Animate only the first row (example using red background in the code sandbox). While this "works", it's not quite suitable. In my production site, updates are not guaranteed to come through one at a time - multiple rows might be inserted at the same time. All new rows should be animated when they are added to the DOM. This can be replicated in the code sandbox by inserting two UUID's at once in the hook.
  3. Not use list virtualization. This of course works fine, but is not suitable. In my production site this list could contain thousands of items.
  4. Reading this previous question. The previous question is sparse of information, does not have a minimal example, and has no useful answers or comments. Additionally, it is over 5 years old.

How is it possible to achieve the result I'm looking for here?

EDIT:

A further attempt I have tried, extending on 2) above:

  • Save a copy of the "old" list of items each render. When an update is received, subtract the length of the old list from the length of the new list (call this number n). Animate the top n items of the new list. This "works", but the production system has some intricacies making this solution insufficient - each list item is dated with an ISO timestamp, and the list is sorted according to timestamp new -> old. Updates received via websocket also have a timestamp, but there is no guarantee the new item will always be newer than the current top of the list - in some cases, new items are inserted into position 2, 3 or further down in the list. In this case, animating the top n items based on the length change would not be accurate.
2

There are 2 best solutions below

0
On

Don´t know how costly it can be for you, but one way to do that is on every state change add a specific className to the new insert rows and a className to the already inserted rows.

So that way you can handle using the className related to the new inserted lines.

Something like this:

const mappedObject = (data, className) => {
  return {
    item: data,
    className
  };
};

React.useEffect(() => {
    const interval = setInterval(() => {
      const alreadyInsertedData = data.map((item) => {
        item.className = "already-inserted";

        return item;
      });

      setData((prev) => [
        mappedObject(uuidv4(), "first-insert"),
        mappedObject(uuidv4(), "first-insert"),
        mappedObject(uuidv4(), "first-insert"),
        ...alreadyInsertedData
      ]);
    }, 3000);

    return () => {
      clearTimeout(interval);
    };
  }, [setData, data]);

Here's a code sample to you check.

0
On

Save a copy of the "old" list of items each render. When an update is received, subtract the length of the old list from the length of the new list (call this number n). Animate the top n items of the new list.

As you said, if the list items have a unique id, then you only need to compare the update of the ids each time you update, and perform animation on the list items corresponding to these added or decreased ids.