简体   繁体   中英

React hooks: How do I update state on a nested object with array with useState()?

I have this form with questions and answers i get from the backend like this:

{
  "id": 0,
  "seq": 0,
  "ccid": "string",
  "topic": "string",
  "riskOutcome": "string",
  "condition": "string",
  "comment": "string",
  "visible": true,
  "created": "2021-10-14T14:12:48.911Z",
  "updated": "2021-10-14T14:12:48.911Z",
  "createdById": 0,
  "updatedById": 0,
  "parentContentTypeId": 0,
  "questionTypeId": 0,
  "subContentTypes": [
    {
      "id": 0,
      "seq": 0,
      "ccid": "string",
      "type": "string",
      "topic": "string",
      "riskOutcome": "string",
      "condition": "string",
      "comment": "string",
      "visible": true,
      "created": "2021-10-14T14:12:48.911Z",
      "updated": "2021-10-14T14:12:48.911Z",
      "createdById": 0,
      "updatedById": 0,
      "parentContentTypeId": 0,
      "subContentTypes": [
        null
      ],
      "textI18Ns": [
        {
          "id": 0,
          "text": "string",
          "languageCode": "string",
          "type": "string",
          "contentTypeId": 0
        }
      ]
    }
  ],
  "textI18Ns": [
    {
      "id": 0,
      "text": "string",
      "languageCode": "string",
      "type": "string",
      "contentTypeId": 0
    }
  ]
}

in the subContentTypes Array are the answers and within textI18Ns is the text for the answers that is shown.

I seperate the answers from the question with this

const [answerData, setAnswerData] = useState();

function getDatafromBackend = () =>{
            dispatch(getOneQuestion(data))
            .unwrap()
            .then((originalPromiseResult) => {
                var options = {};
                Object.entries(originalPromiseResult.subContentTypes).map(([key, value]) =>   {
                    options[key] = value;
                })  
                console.log("answertdata ", options)

                setAnswerData(options);
               }
}

Now what I want to do is, to edit a Text from the Answer State. My JSX looks like this:

let datalist = Object.entries(answerData).map(([key, value]) => {
return (<Paper elevation={3} style={{ margin: 10 }} key={"answerlist"+key}>
                    <Typography style={{ margin: 10}}>{"Daten zur Antwort: "}{answerData[key].ccid} </Typography>
                    {
                        Object.entries(answerData[key].textI18Ns).map(([index,value]) =>{
                            return (
                                <Paper elevation={3} style={{ margin: 10}} key={"questiontext"+index}>
                                    <TextField label={"Text ID"} disabled className="QuestionDataField" value={answerData[key].textI18Ns[index].id} fullWidth></TextField><br />
                                    <TextField label={"Content Typ ID"} disabled className="QuestionDataField" value={answerData[key].textI18Ns[index].contentTypeId} fullWidth></TextField><br />
                                    <TextField label={"Typ"} className="QuestionDataField" value={answerData[key].textI18Ns[index].type} fullWidth onChange={(event) => { updateAnswerTexts(event, 'type', key, index) }}></TextField><br />
                                    <TextField label={"Text"} multiline className="QuestionDataField" value={answerData[key].textI18Ns[index].text} fullWidth onChange={(event) => { updateAnswerTexts(event, 'text',key, index) }}></TextField><br />
                                    <TextField label={"Sprache"} className="QuestionDataField" value={answerData[key].textI18Ns[index].languageCode} fullWidth onChange={(event) => { updateAnswerTexts(event, 'languageCode', key, index) }}></TextField><br />
                                    <Divider/>
                                </Paper>
                            )
                        })
                    )}
setTableBody(datalist)

Here is the Problem. What i tried: Make a copy, change it and set the changed object

    const updateAnswerTexts = (event,prop,key, index) =>{
        let oldstate = answerData[index].textI18Ns[key];
        let updatedstate = {...oldstate, [prop]: event.target.value};
        let newstatetexts =  Object.assign({}, answerData);



        //Here is the Problem. Console says "Uncaught TypeError: 0 is read-only setSTate"
        newstatetexts[index].textI18Ns[key] = updatedstate;
        setAnswerData(newstatetexts)

        setAnswerData((prevState) => {
            prevState[index].textI18Ns[key] = updatedstate;
            return({
                ...prevState
            })
        })
    }
}

Another try: Here the Console says, [prop] is read only

    const updateAnswerTexts = (event,prop,key, index) =>{
       
         setAnswerData((prevState) => {
             prevState[index].textI18Ns[key][prop] = event.target.value;
             return({
                 ...prevState
             })
         })
    }

Another try: Here the Console says, [prop] is read only too

    const updateAnswerTexts = (event,prop,key, index) =>{
        let oldstate = answerData[index].textI18Ns[key];
        let updatedstate = {...oldstate, [prop]: event.target.value};
        setAnswerData((prevState) => {
            prevState[index].textI18Ns[key] = updatedstate;
            return({
                ...prevState
            })
        })
    }

To achieve what you want you'd have to spread every single level of the object, eg.

{
  name: 'john'
  job: {
    name: "chief",
    address: {
      city: "London",
      street: "Baker street"
    }
  }
}

To modify street in useState:

setState(prevState => ({
   ...prevState,
   job: {
      ...prevState.job,
      address: {
         ...prevState.job.address,
         street: "New street"
      }
   }
}))

Sadly this is how it is, you should rather refactor youre state, use class component instead of functional, use some global store, or maybe some library for forms :)

This did it for me

    const updateAnswerTexts = (event,prop,key, index) =>{

        let allanswers = {...answerTextsData};

        let oneanwerobj = {...allanswers[key]};
        
        let answertexts = {...oneanwerobj[index]}
        
        answertexts[prop] = event.target.value;
        oneanwerobj[index] = answertexts;
        allanswers[key] = oneanwerobj
        
        
        setAnswerTextsData(allanswers);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM