I am trying to apply best practices when changing multiple property values of a nested object in my component's state.
My component code currently looks like this:
class App extends Component {
state = {
object: {
a: 10,
b: 7,
c: 12,
}
}
render() {
return (
<div>
<button onClick={this.setState({ object: { a: 5 }})}>change value a </button>
<button onClick={this.setState({ object: { b: 2 }})}>change value b </button>
<button onClick={this.setState({ object: { c: 3 }})}>change value c </button>
</div>
<p>{this.state.object.a}</p>
<p>{this.state.object.b}</p>
<p>{this.state.object.c}</p>
);
}
}
Before any button is clicked, you see three buttons followed by paragraphs reading 10, 7 and 12 on the page.
Once I click the first button labeled "change value a", value b and c are destroyed in my components state object which causes only the value of 5 to show. If I click the second button, then only 2 will show and prop a and c are gone.
I understand the behaviour, however I would like to know the best way to solve it. I want to keep it so all the values showing, and be able to update them in any order while the other values remain.
I am also pretty sure I am mutating the state directly and I know this is bad practice. I just don't know the right practice in this situation.
First to address what you said, "I am mutating the state directly".
You are are not mutating state directly since you are calling this.setState().
Since you want to update a specific part of object: {}, you can use the spread syntax or Object.assign() as follows:
this.setState({ object: { ...this.state.object, a: 5 } })
or
this.setState({ object: Object.assign({}, this.state.object, { a: 5 }) })
Because you called this.setState() in render, you will get the Maximum call stack exceeded error.
I can think of four ways to solve this, I'll show two of those.
onClick={this.setState({
object: { c: 3 }})} to onClick={() => this.setState({ object: { c:
3 }})}One way to do this would be by using the spread ... operator on the nested object to merge the update of say { a: 5 }, with the prior state of the object field:
// Create a new state object, with updated value of "5" for nested field "object.a"
{ object: { ...state.object, a: 5 }}
There are a few ways to incorporate this with setState() - one simple way is via a callback passed to setState():
this.setState(state => { object: { ...state.object, a: 5 }})
This allow you to merge the nested objects prior state, with the state changes such as { a : 5 } on object, without completely replacing all the nested object value in your state.
In the case of your render function, you could update the rendered result like so:
render() {
return (
<div>
<button onClick={ () => this.setState(state => { object: { ...state.object, a: 5 }})}>change value a </button>
<button onClick={ () => this.setState(state => { object: { ...state.object, b: 2 }})}>change value b </button>
<button onClick={ () => this.setState(state => { object: { ...state.object, c: 5 }})}>change value c </button>
</div>
<p>{this.state.object.a}</p>
<p>{this.state.object.b}</p>
<p>{this.state.object.c}</p>
);
}
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