DEV Community

nakzyu
nakzyu

Posted on

React State - Avoiding Object Duplication

When developing on the client side of Web, there are many situations where we need to implement scenarios like:

  • When one item is selected in an array, the information related to that item must be displayed.

In this situation, let's look at how to avoid state duplication and errors.

Code Examples

const initialPeoples = [
  { name: "John", age: 20 },
  { name: "Jenny", age: 26 },
  { name: "Peter", age: 30 },
];

function AgeCounter() {
  const [peoples, setPeoples] = useState(initialPeoples);
  const [selectedPeople, setSelectedPeople] = useState(null);

  return (
    <>
      <ul>
        {peoples.map((people) => (
          <li
            key={people.name}
            onClick={() => {
              setSelectedPeople(people);
            }}
          >
            {people.name}, age {people.age}
          </li>
        ))}
      </ul>
      <button
        onClick={() => {
          setPeoples(
            peoples.map((people) => ({ ...people, age: people.age + 1 }))
          );
        }}
      >
        Happy New Year!
      </button>
      <div>
        selected people is: {selectedPeople?.name}, age {selectedPeople?.age}
      </div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Image description

The code above will work as follows:

  • Three objects that contain information about people exist in initialPeoples.
  • Displays information of those people below the <ul> tag.
  • When a specific person is clicked, the object corresponding to that person is saved in selectedPeople.
    • Displays the selected selectedPeople information at the bottom of the screen.
  • When the Happy New Year! button is clicked, the age of the people increases by one.
    • The age displayed in selectedPeople is also increased by one. At first glance, the code may seem fine, but there is an issue.

When John is selected and the Happy New Year! button is clicked, the age displayed in selectedPeople does not increase.

Image description

The reason for this is that the object saved in selectedPeople and the object I created by clicking the Happy New Year! button are different objects.

<button
  onClick={() => {
    setPeoples(peoples.map((people) => ({ ...people, age: people.age + 1 })));
  }}
>
  Happy New Year!
</button>
Enter fullscreen mode Exit fullscreen mode

setState(setPeoples) does not operate by modifying some properties of the previous object with a pointer to the previous object within the rendering lifecycle, but always forces the creation of a new object.

By using the object creation reservation keyword {} in peoples.map((people) => ({ ...people, age: people.age + 1 })), a new object was saved in the new array created by map. Therefore, the age of the new object in the new array and the object in selectedPeople are not related at all, so clicking the Happy New Year! button does not increase the age.

This is also an anti-pattern that unnecessarily duplicates objects.

How can we modify it?

Replace the state named selectedPeople with a state named selectedPeopleName, which holds the unique name of a person as its value. Then declare selectedPeople as a regular variable and use it as follows:

const initialPeoples = [
  { name: "John", age: 20 },
  { name: "Jenny", age: 26 },
  { name: "Peter", age: 30 },
];

function AgeCounter() {
  const [peoples, setPeoples] = useState(initialPeoples);
  const [selectedPeopleName, setSelectedPeopleName] = useState(null);
  const selectedPeople = peoples.find(
    (people) => people.name === selectedPeopleName
  );

  return (
    <>
      <ul>
        {peoples.map((people) => (
          <li
            key={people.name}
            onClick={() => {
              setSelectedPeopleName(people.name);
            }}
          >
            {people.name}, age {people.age}
          </li>
        ))}
      </ul>
      <button
        onClick={() => {
          setPeoples(
            peoples.map((people) => ({ ...people, age: people.age + 1 }))
          );
        }}
      >
        Happy New Year!
      </button>
      <div>
        selected people is: {selectedPeople?.name}, age {selectedPeople?.age}
      </div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Image description

Now, even if you select John and press the Happy New Year! button, the age will increase properly.

Top comments (0)