Search code examples
javascriptreactjsstatenested-object

React.js : Updating State of Nested Object


Front End - Front End Upon clicking the star, I want to update the state of nested object, with the new rating value of star.

I tried many things but it didnt work as states are immutable.

Nested State

Can some upon please suggest how can I update the value in nested object

onStarClicked = (kTypName, subItemId1, newRating) => {
//console.log(subItemId.split("_"));
let evaluation = subItemId1.split("_")[0];
let subItemId = subItemId1.split("_")[1];
console.log(subItemId);
const r = { ...this.state.ratings };
let kT = r.knowledgeTypes;
let sub = '', kTN = '', kIN = '';
kT.map(knowledgeType => {
  //console.log(knowledgeType.knowledgeTypeId);
  knowledgeType.knowledgeItems.map(knowledgeItem => {
    //console.log(knowledgeItem.knowledgeItemId);
    knowledgeItem.subItems.map(knowledgeSubItem => {
      //console.log(knowledgeSubItem.subItemId);
      if (subItemId === knowledgeSubItem.subItemId) {
        kTN = knowledgeType.knowledgeTypeName;
        kIN = knowledgeItem.knowledgeItemName;
        sub = knowledgeSubItem;
        if (evaluation === "self") {
          sub.evaluation.self.rating = newRating;
        }
        else if (evaluation === "evaluator") {
          sub.evaluation.evaluator.rating = newRating;
        }

        //alert(evaluation + subItemId + ' ' + newRating);
        //return;
      }
    })
  })

});


this.setState({
  ...this.state,
  ratings: {
    ...this.state.ratings,
    knowledgeTypes: [
      ...this.state.ratings.knowledgeTypes,
      this.state.ratings.knowledgeTypes.filter(kt => kt.knowledgeTypeName !== kTN),
      {
        ...this.state.ratings.knowledgeTypes.knowledgeItems.
          filter(ki => ki.knowledgeItemName !== kIN),
        knowledgeItems: {
          ...this.state.ratings.knowledgeTypes.knowledgeItems.subItems.
            filter(si => si.subItemId !== subItemId),
          sub
        }
      }]
  }
});

}


Solution

  • You basically have to create a new empty array of knowledgeTypes and use the current state to find which item of the state you need to change using Object.keys/map/filter functions.

    You'd use the current state in a variable and modify that variable only. You'd likely not mess with the actual state object in any way.

    After you have done that, simply append it to the empty array. Then you can setState() the new array to the actual state property.

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          financialYear: "2019-20",
          quarter: "Q1",
          isCurrentQuarter: true,
          knowledgeTypes: [
            {
              knowledgeTypeName: "Technology",
              knowledgeItems: [
                {
                  knowledgeItemName: "Java",
                  subItems: [
                    {
                      subItemId: "2",
                      subItemName: "Collections",
                      evaluation: {
                        self: {
                          ntnet: "Joe",
                          rating: 1,
                          isEditable: true
                        }
                      }
                    }
                  ]
                }
              ]
            }
          ]
        };
      }
    
      handleClick = e => {
        const { knowledgeTypes } = this.state;
    
        // transformation
        const itemToChange = knowledgeTypes.map(item => {
          if (item.knowledgeTypeName === "Technology") {
            return item;
          }
        });
        const currItems = itemToChange[0].knowledgeItems[0].subItems;
        const subItem = currItems.map(item => {
          if (item.subItemId === "2") {
            return item;
          }
        });
        const person = subItem[0].evaluation;
        person.self.rating = 55; //change
    
        const newKnowledgeTypes = [];
        knowledgeTypes.map(item => {
          if (item.knowledgeTypeName === "Technology") {
            newKnowledgeTypes.push(itemToChange);
          }
          newKnowledgeTypes.push(item);
        });
    
        this.setState({
          knowledgeTypes: newKnowledgeTypes
        });
    
        console.log(this.state);
      };
    
      render() {
        return (
          <div>
            MyComponent
            <button onClick={this.handleClick}>Hello</button>
          </div>
        );
      }
    }
    
    
    

    The sandbox can be found on https://codesandbox.io/s/musing-dew-8r2vk.

    Note: It is advisable you do not use nested state objects because state objects are something more lightweight so that they do not have performance considerations.