Skip to content

React JSX Dynamically changing the state of a component. Best practice and why

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

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 counters 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.