Skip to content
Nate
Stephens

Objects Are Not Really Nested

Objects Are Not Really Nested

Objects within Objects

Here's an example of a object that appears "nested":

const obj = {
  name: 'Niki de Saint Phalle',
  artwork: {
    title: 'Blue Nana',
    city: 'Hamburg',
    image: 'https://i.imgur.com/Sd1AgUOm.jpg',
  },
};

However, “nesting” is an inaccurate way to think about how objects behave. When the code executes, there is no such thing as a “nested” object. You are really looking at two different objects:

const obj1 = {
  title: 'Blue Nana',
  city: 'Hamburg',
  image: 'https://i.imgur.com/Sd1AgUOm.jpg',
};

const obj2 = {
  name: 'Niki de Saint Phalle',
  artwork: obj1,
};

The obj1 object is not “inside” obj2. For example, obj3 could “point” at obj1 too:

const obj1 = {
  title: 'Blue Nana',
  city: 'Hamburg',
  image: 'https://i.imgur.com/Sd1AgUOm.jpg',
};

const obj2 = {
  name: 'Niki de Saint Phalle',
  artwork: obj1,
};

const obj3 = {
  name: 'Copycat',
  artwork: obj1,
};

If you were to mutate obj3.artwork.city, it would affect both obj2.artwork.city and obj1.city. This is because obj3.artwork, obj2.artwork, and obj1 are the same object. This is difficult to see when you think of objects as “nested”. Instead, they are separate objects “pointing” at each other with properties.

Objects within Arrays

Objects are not really located “inside” arrays. They might appear to be “inside” in code, but each object in an array is a separate value, to which the array “points”.

When updating nested state, you need to create copies from the point where you want to update, and all the way up to the top level.

Even if you copy an array, you can’t mutate existing items inside of it directly. This is because copying is shallow — the new array will contain the same items as the original one. So if you modify an object inside the copied array, you are mutating the existing state.

const list = [
  {
    version: 1,
    seen: false,
  },
  {
    version: 2,
    seen: false,
  },
];

const nextList = [...list];
nextList[0].seen = true; // Problem: mutates list[0]

Although nextList and list are two different arrays, nextList[0] and list[0] point to the same object. So by changing nextList[0].seen, you are also changing list[0].seen. This is a state mutation, which you should avoid! You can solve this issue in a similar way to updating nested JavaScript objects — by copying individual items you want to change instead of mutating them.

So instead of:

const myNextList = [...myList];
const artwork = myNextList.find((a) => a.id === artworkId);
artwork.seen = nextSeen; // Problem: mutates an existing item
setMyList(myNextList);

Do:

setMyList(myList.map(artwork => {
  if (artwork.id === artworkId) {
    // Create a *new* object with changes
    return { ...artwork, seen: nextSeen };
  } else {
    // No changes
    return artwork;
  }
});

From the React docs.


Last Updated: