I was trying to animate list insertion and removal with ReactCSSTransitionGroup, but the removal animation always animates only the last item of the list instead of the one that's being removed.
Here's a jsbin to illustrate this problem. Try pressing the "Add" button to verify that the insertion animation indeed works as expected, then click on the "x" besides any item to see the problem where the last item of the list is animated instead of the one you tried to remove.
Did I do something wrong while setting up the TransitionGroup or am I missing something in the CSS transition definitions?
To delete an item from list with React and JavaScript, we can use some array methods. to create the items state with useState . Then we define the deleteItem function that takes the index of the item to delete and returns a function that calls setItems with (items) => items. filter((_, i) => i !==
You're experiencing this problem because you're using index
as your key:
let nodes = items.map((item, index) => {
let idx = index
return (<Item key={index} value={item} index={index} _delete={this._onDelete}/>)
})
React uses the key
property during virtual DOM diffing to figure out which element was removed, but indexes will never serve this purpose sufficiently.
Consider this example: you start off with the following array, which results in the following DOM structure:
const arr = [2, 4, 6, 8];
<li key={0}>2</li>
<li key={1}>4</li>
<li key={2}>6</li>
<li key={3}>8</li>
Then imagine you remove the element at index 2
. You now have the following array, and the following DOM structure:
const arr = [2, 4, 8];
<li key={0}>2</li>
<li key={1}>4</li>
<li key={2}>8</li>
Notice that the 8
now resides in index 2
; React sees that the difference between this DOM structure and the last one is that the li
with key 3
is missing, so it removes it. So, no matter which array element you removed, the resulting DOM structure will be missing the li
with key 3
.
The solution is to use a unique identifier for each item in the list; in a real-life application, you might have an id
field or some other primary key to use; for an app like this one, you can generate a incrementing ID:
let id = 0;
class List extends Component {
constructor() {
this.state = {
items: [{id: ++id, value: 1}, {id: ++id, value: 2}]
}
// ...
}
_onClick(e) {
this.state.items.push({id: ++id, value: Math.round(Math.random() * 10)})
this.setState({items: this.state.items})
}
// ...
render() {
let items = this.state.items
let nodes = items.map((item, index) => {
let idx = index
return (<Item key={item.id} value={item.value} index={index} _delete={this._onDelete}/>)
})
// ...
}
}
Working example: http://jsbin.com/higofuhuni/2/edit
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With