0

Changing a deeper property of an object shows in console but not when we open it or access it later.The below is my object :

 section@shikhar:
1: {displayName: "Entity", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(24), template: null, …}
2:
customTooltip: null
defaultValue: null
dependentValues: {AVS - Avionics: Array(4), DMS - Defence Mission Systems: Array(4), LAS - Land and Air Systems: Array(5), SIX - Secure Communications & Information Systems: Array(4), GTS - Ground Transportation Systems: Array(4), …}
displayName: "Business Line"
dropdownType: "SINGLESELECT"
fieldType: "DROPDOWN"
isDashboard: true
isOptional: false
isParentOf: "3"
isReporting: true
isTooltipEnabled: null
parent: 1
parentValue: ["depend"]
productTooltip: null
sequenceNumber: 2
template: null
values: []
__proto__: Object
3: {displayName: "Product Line Family", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(0), template: null, …}
4: {displayName: "Product Line", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(0), template: null, …}
5: {displayName: "Brief Instructions", dropdownType: null, fieldType: "TEXT", values: null, template: null, …}
6: {displayName: "Main message", dropdownType: null, fieldType: "TEXT", values: null, template: null, …}
7: {displayName: "Tone of voice", dropdownType: "MULTISELECT", fieldType: "DROPDOWN", values: Array(10), template: null, …}
8: {displayName: "Audience to address (Primary)", dropdownType: "MULTISELECT", fieldType: "DROPDOWN", values: Array(2), template: null, …}
9: {displayName: "Target to reach (Secondary)", dropdownType: "MULTISELECT", fieldType: "DROPDOWN", values: Array(9), template: null, …}
10: {displayName: "Sub-type", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(2), template: null, …}
11: {displayName: "Size", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(0), template: null, …}
12: {displayName: "Custom Size", dropdownType: null, fieldType: "TEXT", values: Array(0), template: null, …}
13: {displayName: "Copyrights: Expiry Date", dropdownType: null, fieldType: "DATE", values: null, template: null, …}
14: {displayName: "Output Format", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(4), template: null, …}
15: {displayName: "Other", dropdownType: null, fieldType: "TEXT", values: Array(0), template: null, …}
16: {displayName: "Tags", dropdownType: null, fieldType: "TEXT", values: null, template: null, …}

Here the problem is that even though I am setting the values inside the "2" object it still shows it as [] but actually it should have 4 values like :

{displayName: "Business Line", dropdownType: "SINGLESELECT", fieldType: "DROPDOWN", values: Array(4), template: null, …

I know the object is getting mutated somehow because even the console says that the value was evaluated just now.

The code I am using being :

 updateAllCommonValues=(val,id)=>{
    const { commonTemplateFields } = this.state;

    var commonTemplateValues = JSON.parse(JSON.stringify(commonTemplateFields));
    _.forEach(commonTemplateValues,(section,k)=>{
        _.forEach(section,(field,key)=>{
             if(key == id){
            field.value = val
                 if(field.isParentOf != null){
                    commonTemplateValues[k][Number(field.isParentOf)]['values'] = section[field.isParentOf].dependentValues[val];
                 }
            }
        })
    })
    this.setState({
        commonTemplateFields:commonTemplateValues
    },()=>{
    this.props.updateFinalPojo('orderInfo',commonTemplateValues);

    })
}

The correct value of commonTemplateFields is not coming to setstate i.e. the array for values instead it is empty as the initial state.

PS: It passes all if conditions.

PPS : Object structure :

    {  

   "values":[  
      "Flight Avionics (FLX)",
      "In-Flight Entertainment (IFE)",
      "Training & Simulation (T&S)",
      "Microwave & Imaging (MIS)"
   ],
   "parent":1,
   "parentValue":[  
      "depend"
   ],
   "dependentValues":{  
      "AVS - Avionics":[  
         "Flight Avionics (FLX)",
         "In-Flight Entertainment (IFE)",
         "Training & Simulation (T&S)",
         "Microwave & Imaging (MIS)"
      ],
      "DMS - Defence Mission Systems":[  
         "Above Water Systems (AWS)",
         "Electronic Combat Systems (ECS)",
         "Intelligence Surveillance & Reconnaissance (ISR)",
         "Under Water Systems (UWS)"
      ],

   },
   "isParentOf":"3"
}
SHIKHAR SINGH
  • 419
  • 6
  • 17
  • 1
    `JSON.parse(JSON.stringify(...))` is a **terrible**, lossy way to clone an object tree. See [this question's answres](https://stackoverflow.com/questions/122102/) for better ways. There's also no reason to clone the entire tree just to update state (only clone the parts that change). This code also breaks one of the fundamental React rules: Because [state changes are asynchronous](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous), if you're setting state based on existing state, you **must** use the callback version of `setState`. – T.J. Crowder Jul 16 '19 at 09:56
  • 1
    Why call it `commonTemplateFields` in one place but `commonTemplateValues` in another? – T.J. Crowder Jul 16 '19 at 09:57
  • Could be [this problem](http://stackoverflow.com/questions/38660832/element-children-has-elements-but-returns-empty-htmlcollection), but really we'll need a [mcve] demonstrating the problem in order to help you. You can do a runnable MCVE using Stack Snippets (the `[<>]` toolbar button). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder Jul 16 '19 at 09:58
  • Apologies, editing with the correct variable. – SHIKHAR SINGH Jul 16 '19 at 10:01
  • We also need a better idea of the data structure. (But, again, minimal -- we don't need all 14 fields or whatever, just the ones involved in the code in question.) – T.J. Crowder Jul 16 '19 at 10:06
  • Added the object structure. – SHIKHAR SINGH Jul 16 '19 at 10:32
  • That's good, but again, we need a [mcve] to help you, with only the required bits. Also, that's the structure of **what** object? We need the structure starting at `componentFieldValues` so we know what sections and fields are. But again: **Minimal versions of them**, with a [live example](http://meta.stackoverflow.com/questions/338537/) of the problem. – T.J. Crowder Jul 16 '19 at 10:37
  • EDited again in pps – SHIKHAR SINGH Jul 16 '19 at 10:53

2 Answers2

0

There isn't enough in your question to know the structure of what you're updating precisely, but in general, JSON.parse(JSON.stringify(...)) is a lossy way to clone objects (also slow, as it makes a trip through JSON). There's also no need to clone the entire structure, just the parts you're changing.

Separately, any time you're setting state based on existing state, you must use the callback version of setState, because state updates are asynchronous so if you don't use the callback version, you could be using stale source state information.

If I'm reading your code correctly, commonTemplateFields is either an array or an object containing sections, which are either arrays or objects containing fields, which are objects. So here's how you would update the field(s) whose field key is id (see comments):

updateAllCommonValues = (val,id) => {
    // Must use callback version of `setState`
    this.setState(
        ({commonTemplateFields}) => {
            // Get a shallow copy of `commonTemplateFields`
            commonTemplateFields = {...commonTemplateFields}; // Or `= [...commonTemplateFields]` if it's an array

            // Loop through the sections and their fields
            _.forEach(commonTemplateFields, (section, sectionKey) => {
                _.forEach(section, (field, fieldKey) => {
                    if (fieldKey == id) {
                        // Need to update this field, shallow copy the section
                        section = commonTemplateFields[sectionKey] = {...section}; // Or = `[...section]`, I can't tell whether section is an object or array
                        // Shallow copy and update the field
                        field = section[fieldKey] = {...field, value: val};
                        if (field.isParentOf != null) {
                            // Shallow copy and update the dependent field
                            const index = Number(field.isParentOf);
                            section[index] = {...section[index], values: section[index].dependentValues[val]};
                        }
                    }
                });
            });

            // Return the update
            return {commonTemplateFields};
        },
        () => {
            this.props.updateFinalPojo('orderInfo', this.state.commonTemplateFields);
        }
    );
}

Hopefully, between the JSON thing and the async thing, that helps sort out the issue.


Side note: FWIW, in modern JavaScript there's no need for _.forEach, and using _.forEach can make the code less readable by hiding the structure of your data. If you're looping through an array, you can use the built-in forEach or a for-of loop as appropriate (the for-of might be on the array itself, or on the result of calling its entries method if you need both value and index). If you're looping through the contents of an object, you can use for-of loop on Object.keys, Object.values, or Object.entries depending on what you're doing.

Obviously, though, if you simply prefer _.forEach, that's a style choice you're entirely entitled to make. :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • HI @T.J. Crowder, thankyou so much for your valuable inputs. However when I print the state in render using console.log I still get the values as empty. – SHIKHAR SINGH Jul 16 '19 at 10:32
0

I figured out the solution to this and posting this as it might be helpful to someone else as well. When we do looping over nested object it appears as if the comparision is made only on the first level. The property I was changing was at 3rd level. Maybe I am wrong but this is the conclusion I have arrived on coz when I made another change at the first level, the value change at 3rd level started to show as well.

SHIKHAR SINGH
  • 419
  • 6
  • 17