Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
93 views
in Technique[技术] by (71.8m points)

reactjs - Issue with state update approach for nested objects

Major EDIT

I have quite huge object which is 3 level deep. I use it as a template to generate components on the page and to store the values which later are utilized, eg:

obj =
 { 
  "group": {
    "subgroup1": {
      "value": {
        "type": "c",
        "values": []
      },
      "fields_information": {
        "component_type": "table",
        "table_headers": [
          "label",
          "size"
        ],
      }
    },
    "subgroup2": {
      "value": {
        "type": "c",
        "values": []
      },
      "fields_information": {
        "component_type": "table",
        "table_headers": [
          "label",
          "size"
        ],
      }
    },
  },
 }

Thanks to this I can dynamically generate view which is, as a template, stored in DB.

I'm struggling with 2 things. Firstly, updating values basing on user input for textbox, checkboxes and similar. I'm doing it this way:

    const updateObj = (group, subgroup, value) => {
        let tempObj = {...obj}
        tempObj[group][subgroup].value.value = value
        toggleObj(tempObj)
    }

I know that the spread operator is not in fact doing deep copy. However it allows me to work on the object and save it later. Is that an issue? Do I have to cloneDeep or it is just fine? Could cloneDeep impact performance?

Second case is described below

export const ObjectContext = React.createContext({
    obj: {},
    toggleObj: () => {},
});

export const Parent = (props) => {
    const [obj, toggleObj] = useState()
    const value = {obj, toggleObj}

    return (
        <FormCreator />
    )
}

const FormCreator = ({ catalog }) => {
    const {obj, toggleObj} = React.useContext(ObjectContext)

    return (<>
        {Object.keys(obj).map((sectionName, sectionIdx) => {
        const objFieldsInformation = sectionContent[keyName].fields_information
        const objValue = sectionContent[keyName].value
        ...
            if (objFieldsInformation.component_type === 'table') {
            return (
                <CustomTable 
                key={keyName + "id"}
                label={objFieldsInformation.label}
                headers={objFieldsInformation.table_headers}
                suggestedValues={[{label: "", size: ""}, {label: "", size: ""}, {label: "", size: ""}]}
                values={objValue.values}
                sectionName={sectionName}
                keyName={keyName}/>
            )
            }
        ...
        })}
    </>)
}

const CustomTable= (props) => {
    const { label = "", headers = [], suggestedValues = [], values, readOnly = false, sectionName, keyName } = props
    const {obj, toggleObj} = React.useContext(ObjectContext) 
    
    //this one WORKS
    useEffect(() => {
        if (obj[sectionName][keyName].value.type === "complex") {
            let temp = {...obj}
            temp[sectionName][keyName].value.values = [...suggestedValues]
            toggleObj(temp)
        }
    }, [])
    
    //this one DOES NOT
    useEffect(() => {

        if (obj[sectionName][keyName].value.type === "c") {
            let temp = {...obj, [sectionName]: {...obj[sectionName], [keyName]: {...obj[sectionName][keyName], value: {...obj[sectionName][keyName].value, values: [{label: "", size: ""}, {label: "", size: ""}, {label: "", size: ""}]}}}}
            toggleObj(temp)
        }
    }, [])
    
    return (
    //draw the array
    )
}

Please refer to CustomTable component. As on the example Object above, I have 2 CustomTables to be printed. Unfortunately, one useEffect that should work is not working properly. I'm observing, that values field is set only for the last "table" in Obj. When I'm doing shallow copy of obj, it works fine. But I'm afraid of any repercussion that might happens in future.

I'm also totally new to using createContext and maybe somehow it is the issue.

Kudos to anyone understanding that chaos :)

question from:https://stackoverflow.com/questions/65909885/issue-with-state-update-approach-for-nested-objects

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The main issue appears to be that you are not providing your context. What you have is literally passing the blank object and void returning function. Hence why calling it has no actual effect, but mutating the value does.

export const ObjectContext = React.createContext({
    obj: {},
    toggleObj: () => {},
});

export const Parent = (props) => {
    const [obj, toggleObj] = useState({})
    const value = {obj, toggleObj}

    return (
        <ObjectContext.Provider value={value}>
           <FormCreator />
        </ObjectContext.Provider>
    )
}

Ideally you would also make this component above wrap around FormCreator and render it as props.children instead. This is to prevent the entire sub-tree being rerendered every time toggleObj is called. See the first part of this tutorial to get an idea of the typical pattern.

As to the question about mutating state, it absolutely is important to keep state immutable in React - at least, if you are using useState or some kind of reducer. Bugs arising from state mutation come up all the time on Stack Overflow, so often in fact that I recently made a codesandbox which demonstrates some of the more common ones.

I also agree with @SamuliHakoniemi that a deeply nested object like this is actually better suited to the useReducer hook, and might even go one further and suggest that a proper state management library like Redux is needed here. It will allow you to subdivide reducers to target the fragments of state which actually update, which will help with the performance cost of deeply cloning state structure if or when it becomes an actual issue.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...