I have an app class with items array, that should have items with unique ids, that is updated when a button is clicked:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
saveId: 1,
items: []
};
}
addItem(name) {
let item = {
id: this.state.saveId,
name: name
}
this.setState({saveId: this.state.saveId + 1});
let items = this.state.items;
items.push(item);
this.setState({items: items});
}
updateCount() {
this.addItem("one");
this.addItem("two");
this.addItem("three");
}
render() {
return (
<div>
<button
onClick={() => this.updateCount()}
>
SaveId: {this.state.saveId}
</button>
<ul>
{this.state.items.map((item, i) => {
return <li key={item.id}>{item.name + "" + item.id}</li>
})}
</ul>
</div>);
}
}
export default App;
when the button is clicked the addItem is called three times, updating the state of the last id saved. after the updateCount function has finished:
this.state.saveId = 2
this.state.items = [
{
name: "one",
id: 1
},
{
name: "two",
id: 1
},
{
name: "three",
id: 1
}
]
That generate a "Warning: Encountered two children with the same key, 1
.".
I know that setState is asynchronous and they are called in batch when called in a loop.
My question is:
Is there a way to bypass it without changing the state directly with this.state.saveId++
instead?
Thanks.
Use the function version of setState. It will pass you in the previous state, and you can use that to craft your new state. If multiple set states happen in rapid succession, you can be sure you'll only get the most recent state, not the state before the set states started happening. Additionally, you're currently mutating the old state when you push to items. Instead, create a new array, and add to that.
addItem(name) {
this.setState(prevState => {
let item = {
id: prevState.saveId,
name: name,
}
// shallow copy of the array, with an extra item at the end
const newItems = [...prevState.items, item];
return {
items: newItems,
saveId: prevState.saveId + 1,
}
});
}