Updating nested state properties in React JS

To update nested state properties in React JS, you can make a copy of state using library like Lodash and store it using setState or useState hook function with required changes.

But we should always avoid using nested state. This is because in pure components, React will face difficulties in understanding if state changed or not. Or, it may false re-render the unchanged states which is bad for performance optimization.

Consider this example –

this.state = {
  superhero: {
    favorite: 'Captain America'
  }
}

this.setState({
  superhero: {
    favorite: 'Ironman'
  }  
})

When you update a state, you need to provide the complete tree to this.setState. For example, you state has multiple properties like –

this.state = {
  superhero: {
    favorite: 'Captain America'
  },
  group: {
    avengers: 'Yes'
  }  
}

Now, if you want to change favorite of superhero to 'Ironman', you can’t only pass the nested property in this.setState. So, this is wrong –

this.setState({superhero.favorite: 'Ironman'})

But you don’t need to pass the whole state to update one tree. So, if superhero favorite is changed, you don’t need to touch group property. This is completely valid –

this.setState({
  superhero: {
    favorite: 'Ironman'
  }
})

So, as this one –

this.setState({
  superhero: {
    favorite: 'Ironman'
  },
  group: {
    avengers: 'Yes'
  }
})

If your state is complex and is deeply nested then you may make of deep copy of it using Lodash cloneDeep() function. It is necessary to make a copy because states should not be mutated. They should only be updated using setState.

import { cloneDeep } from "lodash";
import React from "react";

export default class App extends React.Component {
  state = {
    superhero: {
      favorite: "Captain America"
    },
    group: {
      avengers: "Yes"
    }
  };

  changeState = () => {
    const newState = cloneDeep(this.state);
    newState.superhero.favorite =
      newState.superhero.favorite === "Captain America"
        ? "Ironman"
        : "Captain America";

    this.setState(newState);
  };

  render() {
    return (
      <div className="App">
        <p>Current State: </p>
        <pre>{JSON.stringify(this.state, null, "  ")}</pre>
        <p>
          <button onClick={this.changeState}>
            Change State favorite:
            {this.state.superhero.favorite === "Captain America"
              ? "Ironman"
              : "Captain America"}
          </button>
        </p>
      </div>
    );
  }
}

    Tweet this to help others

Live Demo

Open Live Demo