I am pretty new at React and am following a video on youtube and working along + adding some more on top of it.
I have a button which when clicked, it decreases the value
of object. In the video, the teacher copies the state to a new array, finds the index of the item, does the manipulation needed, and then sets the state again using this.setState()
This is the hardcoded state which I am using to practice. I want that when a button is clicked, the value
to be reduced by 1 unless the value is smaller than or equal to 0
.
state = { counters: [ { id: 1, name: "Drink 1", value: 4 }, { id: 2, name: "Drink 2", value: 0 }, { id: 3, name: "Drink 3", value: 0 }, { id: 4, name: "Drink 4", value: 0 }, ], }; //and what I'm passing down to other components: {this.state.counters.map((counter) => ( <Counter key={counter.id} counter={counter} onDeletion={this.handleDeletion} onIncrement={this.handleIncrement} onDecrement={this.handleDecrement} ></Counter> ))}
Code from video:
handleDecrement = (counter) => { const counters = [...this.state.counters]; const indexOfCounters = counters.indexOf(counter); counters[indexOfCounters] = { ...counter }; counters[indexOfCounters].value <= 0 ? (counters[indexOfCounters].value = 0) : counters[indexOfCounters].value--; this.setState({ counters }); };
Here is the code which I came up with that gives the button the same functionality:
handleDecrement = (counter) => { counter.value <= 0 ? (counter.value = 0) : counter.value--; this.setState(counter); };
Both ways provide the functionality needed, I am just hesitant to go my way in case this goes against best practice.
From reading the docs and similar state related questions, I can guess that the way provided in the video changes the complete state and my way only changes an object within it. Is the youtube code the correct way to approach this because the whole state is being set and we keep a single source of truth? Is my way bad practice?
Video is by Programming with Mosh btw: https://www.youtube.com/watch?v=Ke90Tje7VS0
Advertisement
Answer
I think you’re just confused on the reason behind updating the counters
array instead of an object(counter
) inside it.
It is because the state
should always be updated in an immutable way. In your case, since the value
is in an object that is in turn in an array, the counters
array and the counter
object should have a new reference after updating to properly inform React that the state
has changed.
In your snippet this.setState(counter);
will overwrite the state with the counter
(so the other counter
s are removed) and you’re also mutating the object.
If you want to make the code a bit concise while also making sure that the state is updated immutably, here’s an alternative:
handleDecrement = (counter) => { this.setState(prevState => ({counters: prevState.counters.map(c => { return (c.id === counter.id && c.value > 0) ? {...c, value: c.value - 1} : c; })})); };
In the above snippet map
creates a new array and the {}
object literal syntax creates a new counter
object.