React: List items with unique `key` get re-rendered when order of items changes.

Created on 15 Jul 2018  ·  3Comments  ·  Source: facebook/react

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
When rendering a list of items with key set as unique property on each item element, if order of items changes from previous render, the item elements whose indexes changed are re-rendered(new instance is created) instead of re-using existing instance(mapped via key).

For e.g https://codesandbox.io/s/r5p48kzop
Rendering a list boxes with each box having id, top, left & color props. Every 750ms we update the (top, left) values for all the elements inside boxes array.

While updating if the order of elements doesn't change then every box animates to it's new position. However if we uncomment line 31 which shuffles the boxes, then only elements which retain their position from previous render animate to new positions.

You can observe this behaviour in the sandbox. With _shuffle on sometimes boxes jump to their new positions while with _shuffle off they always animate.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
https://codesandbox.io/s/r5p48kzop

What is the expected behavior?
Regardless of the order of boxes all the box elements should animate to their new positions since they have unique key property set on them.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.3.2 & 16.4.1, Chrome, MacOS.

Most helpful comment

Hey @dhruvparmar372!

This seems to be a limitation of the DOM. To demonstrate this, I created this JSFiddle which changes the order of two DOM nodes and then applies a class to trigger a transition.

It turns out, you can work around this issue by triggering a reflow between changing the order and updating the animation properties.

I added a bit of code to your CodeSandbox make it visible when a component is recreated by React by changing the <Box /> to be a class component and creating a unique id in the constructor. As you can see, the id will stay consistent: https://codesandbox.io/s/jn42wvl343

In addition to that, I applied the reflow workaround to your example by splitting the update into two setStates. The first one will change the order, then we trigger a reflow, and afterwards change the position. This seems to work fine in Chrome, Firefox, Safari, and Edge (CodeSandbox does not support IE11 so I could not test on that browser).

All 3 comments

Hey @dhruvparmar372!

This seems to be a limitation of the DOM. To demonstrate this, I created this JSFiddle which changes the order of two DOM nodes and then applies a class to trigger a transition.

It turns out, you can work around this issue by triggering a reflow between changing the order and updating the animation properties.

I added a bit of code to your CodeSandbox make it visible when a component is recreated by React by changing the <Box /> to be a class component and creating a unique id in the constructor. As you can see, the id will stay consistent: https://codesandbox.io/s/jn42wvl343

In addition to that, I applied the reflow workaround to your example by splitting the update into two setStates. The first one will change the order, then we trigger a reflow, and afterwards change the position. This seems to work fine in Chrome, Firefox, Safari, and Edge (CodeSandbox does not support IE11 so I could not test on that browser).

hey @philipp-spiess thanks for clarifying the issue and the examples for the workaround.

Hi, @philipp-spiess

I have the same problem with sorting list 😔
Unfortunately, your Codesandbox example above isn't working anymore(

Here is an example:
https://jsfiddle.net/9odLvbrx/
It sorts items on click with simple CSS transition.
The issue is that transition appears only on the selected item and omits the rest of them.

I know how to work around the issue (commented in the code) but it works fine only on lists which items quantity never changes. So If I want to add or remove item - the stuff becomes incredibly complex. How can I use your reflow overload solution to make it happen in my example?

Thanks in advance! 😊

Was this page helpful?
0 / 5 - 0 ratings