Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React setState for multi level state object

I'm trying to update my state object which contains multiple levels. See this example https://codesandbox.io/s/oq9kp9vy5y.

The problem is, that the values in the render method, does not update ... only the first one. Can I force an update or is there a better way?

Thanks Kasper

like image 478
Kasper Gantzhorn Avatar asked Apr 12 '26 14:04

Kasper Gantzhorn


2 Answers

In your initial state, you have the kitchen object inside the calculatedUsage object. But in your setState call, you have the kitchen object outside of calculatedUsage.

Also, when you are accessing the previous state within setState, it's best to use the functional version of setState, e.g.:

componentDidMount() {
  this.setState(
    prevState => ({
      caclulatedUsage: {
        ...prevState.caclulatedUsage,
        bath: 12,
        kitchen: {
          ...prevState.caclulatedUsage.kitchen,
          stove: 14,
          fridge: 23,
          dishwasher: 34
        }
      }
    }),
    () => {
      console.log(this.state);
    }
  );
}

The reason for this is that setState can be asynchronous, which means that accessing this.state within it may not give you the value you expect. Using the functional version for transactional state changes guarantees consistent results.

You may also want to take a look at the immer library. It makes updating deeply nested state much easier, by allowing you to do immutable updates with a mutable API. Here's a fork of your codesandbox example that uses immer: https://codesandbox.io/s/180p7p0o84

like image 195
Shane Cavaliere Avatar answered Apr 16 '26 19:04

Shane Cavaliere


Try something like this

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      caclulatedUsage: {
        bath: 0,
        kitchen: {
          stove: 0,
          fridge: 0,
          dishwasher: 0
        },
        livingroom: {
          tv: 0,
          tvBox: 0
        }
      }
    };
  }

  componentDidMount() {
    this.setState(
      {
        caclulatedUsage: Object.assign({}, this.state.access, {
          caclulatedUsage: {
            ...this.state.caclulatedUsage,
            bath: 12
          },
          kitchen: {
            ...this.state.caclulatedUsage.kitchen,
            stove: 14,
            fridge: 23,
            dishwasher: 34
          }
        })
      },
      () => {
        console.log(this.state);
      }
    );
  }

  render() {
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        {this.state.caclulatedUsage.bath}
        <br />
        {this.state.caclulatedUsage.kitchen.stove}
        <br />
        {this.state.caclulatedUsage.kitchen.fridge}
        <br />
        {this.state.caclulatedUsage.kitchen.dishwasher}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
like image 31
TRomesh Avatar answered Apr 16 '26 20:04

TRomesh