I am not sure if it´s a react issue or not but I am struggling a lot with this issue. I tried to create just a simple example to post out of my project:
https://code
Edit: Please avoid using your ids as keys as the other answers suggest if multiple of the same elements can be added as your initial example suggests because keys need to be unique and this way they can (and will) be repeated!
Your problem lies in the fact that you are using the key attribute wrongly. React expects your components to have constant keys, so it can decide when and which ones it needs to update.
If you are modifying an array, the keys of the objects are changing, and the element that had the key '1' before the removal is gone afterwards and the component that previously had key '2' becomes the one with key '1' and so forth.
The React documentation advises against using the index as key as well:
"We don’t recommend using indexes for keys if the items can reorder"
To avoid this you should use some sort of unique key generation and store each components key in it's own object, for example like this:
In the constructor:
this.nextKey = 0
When adding a new component:
tempArr.push({comp: comp, id: e.target.id, key: this.nextKey++});
In the render function:
return <li style={styles} key={filter.key}>{filter.comp}</li>
I modified your example to include these lines, you can find it here: https://codepen.io/Isti115/pen/MEmMZP?editors=1111
Also, some other tips:
When creating an object that has a key with a value assigned from a variable with the same name, you can shorten it so that this:
{comp: comp, id: e.target.id}
becomes this:
{comp, id: e.target.id}
.
If you need something more robust, you could use a separate unique key generator like this:
Create GUID / UUID in JavaScript?
or this:
https://gist.github.com/gordonbrander/2230317
ps.: The switch statement you are using is completely useless right now. What have you tried to accomplish with it?
The problem is that you're using the array index as your key
, so React will reuse the first two li
elements and drop the last one. Change key={index}
to key={filter.id}
, and it works as you would expect.
Update concerning the comment & downvote: I assumed uniqueness on filters in the actual code, given that the field is called id
. The CodePen seems more of a stripped down version to show the problem. But if you do actually wish to let each button create multiple text fields, you'd indeed need to add something extra to distinguish the keys (e.g. a counter). This doesn't affect the problem as stated in the question though.
Looking at the code again, I noticed getInput
would be an ideal candidate to extract into a separate (stateless) component, e.g. FilterInput
. This fits better with the react model than keeping child renderings in the component state.
The code produces 3 textboxes in divs. These textboxes are updated by entering the numbers (100, 200, 300). When you click the RemoveFirstButton, the state, which stores these components, is updated and render is called.
The render function does a diff of the current state and the previous state and removes the last div, which contains the number 300. This is because, for the render function, the first element of the array changed from FirstButton to SecondButton, the second element changed from SecondButton to ThirdButton and the third element does not exist anymore.
To make it work as expected, you need to change the key of the elements from the index of the array to the id of the element, so that the render method can tell the difference between the elements.