Flumen
Flumen

Reputation: 169

How to change component order while keeping state?

Scenario

I have a component that may rerender subcomponents in different orders, depending on user input. These subcomponents have state.

Problem

When the order changes, the state from one subcomponent ends up in another. As an example, let's say that on the first render I have the subcomponents A and B, in that order. On the next render, the order changes to B and A. The state that was in A is now in B, and vice-versa.

I made a simpler example in Code Sandbox to illustrate the situation. The list keeps the same order even though it is reversed in the next render.

I imagine this has to do with the way that React maintains state across different components, in which the order of hooks matter.

Should React be able to deal with this scenario? Is the problem in my code? If so, what should be the correct way to rerender components in a different order?

Upvotes: 5

Views: 1245

Answers (1)

Dylan Kerler
Dylan Kerler

Reputation: 2187

This is to do with the way react handles elements in its virtual DOM.

The Solution: Add a unique key (not using an index) and it will work.

<StatefulComponent param={"Tomato"} key={"Tomato"} />,
<StatefulComponent param={"Potato"} key={"Potato"}/>

Why does this behaviour happen?

By default, if no key is specified, react will give each component in a list a key with the value of the index order that it was initially rendered in - This is what is causing the issue. Because you haven't defined an id, React is implicitly making your components into this:

<StatefulComponent param={"Tomato"} key={0} />,
<StatefulComponent param={"Potato"} key={1}/>

And after the array reverse():

<StatefulComponent param={"Potato"} key={0} />,
<StatefulComponent param={"Tomato"} key={1}/>

The reason you see the unexpected behaviour is because react uses keys to identify each element. So if you change your array order after the original render then apply an index as the id on each render, react will get the element that was first rendered with that index as it's key and put it in that place. By changing the key to a unique identifier, react does not get the elements mixed up (since the unique identifier never changes with respect to its element) and can now accurately render each item in the order you intend.

Example: We render a list:

<div id=1>Foo</div> // Id "1" is now bound to this element
<div id=2>Bar</div>

So if you then reorder the list like this (note the id changes):

<div id=1>Bar</div> // this will be transformed to <div id=1>Foo</div>
<div id=2>Foo</div> // this will be transformed to <div id=2>Bar</div>

Then react will transform the elements into whatever id they were assigned to first - This is why it's important to have unique identifiers.

You can read more here: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

Upvotes: 3

Related Questions