Javascript 嵌套对象存储为状态变量,在修改旧值时删除新值

Javascript 嵌套对象存储为状态变量,在修改旧值时删除新值,javascript,reactjs,Javascript,Reactjs,我有一个react组件,它将用户输入值传递给props函数。父函数只是将这些输入附加到对象。但是,当修改旧值时,将删除所有新值。请参考屏幕截图 这是家长。ExamArea.js import McqQuestion from './McqQuestion' import React, { useState, useEffect } from 'react'; import './ExamArea.css' function ExamArea(props) { const[curren

我有一个react组件,它将用户输入值传递给props函数。父函数只是将这些输入附加到对象。但是,当修改旧值时,将删除所有新值。请参考屏幕截图

这是家长。ExamArea.js

import McqQuestion from './McqQuestion'
import React, { useState, useEffect } from 'react';
import './ExamArea.css'

function ExamArea(props) {

    const[currentQuesID, setCurrentQuesID] = useState(2);
    const[mcqQuestionList, setmcqQuestionList] = useState([<McqQuestion returnfunc={returnFromMcqQuestion} id={1}/>]);
    const[ques, setQues] = useState({});


    function returnFromMcqQuestion(quesID, thisQuestion) {
        var temp = {...ques};
        temp["ques"+quesID] = thisQuestion;
        console.log(temp);

        setQues(temp);
      }

    function generateMCQ(questionid) {
        return (<McqQuestion returnfunc={returnFromMcqQuestion} id={questionid}/>)
    }


    function addAnotherQuestion() {
        setmcqQuestionList(mcqQuestionList.concat(generateMCQ(currentQuesID)));
        setCurrentQuesID(currentQuesID+1);
     }
        

    return (

        <div className="ExamArea">
            {mcqQuestionList}
            <button onClick={()=>addAnotherQuestion()} class="add_another_question_button">+ Add Another Question</button>
        </div> 

        );
    

}

export default ExamArea;
import McqQuestion from./McqQuestion'
从“React”导入React,{useState,useffect};
导入“./ExamArea.css”
功能测试(道具){
const[currentQuesID,setCurrentQuesID]=useState(2);
const[mcqQuestionList,setmcqQuestionList]=useState([]);
const[ques,setQues]=useState({});
函数returnfromCqQuestion(quesID,thisQuestion){
var temp={…ques};
临时[“ques”+quesID]=这个问题;
控制台日志(temp);
设定值(温度);
}
函数generateMCQ(问题ID){
返回()
}
函数addAnotherQuestion(){
setmcqQuestionList(mcqQuestionList.concat(generateMCQ(currentQuesID));
setCurrentQuesID(currentQuesID+1);
}
返回(
{mcqQuestionList}
addAnotherQuestion()}class=“添加另一个问题按钮”>+添加另一个问题
);
}
导出默认ExamArea;
这就是那个孩子

import './McqQuestion.css'
import React, { useState, useEffect } from 'react';
import { Paper, TextField } from '@material-ui/core';
import InputBase from '@material-ui/core/InputBase';

/*
This is the component that lets the maker create the question, and then stores the question to packedQuestion.
packedQuestipn is in a format which can be directly sent to the API to be uploaded to the database.
A basic question has question_text, question.title

Props passed:
props.id = The Question ID.
props.returnfunc = The function that gets called with packedQuestion and props.id when everything is done. 
                    props.returnfunc(props.id, packedQuestion) is the thing that is called.

*/


function McqQuestion(props) {

    
    const [packedQuestion, setPackedQuestion] = useState({});
    useEffect(()=> props.returnfunc(props.id, packedQuestion));


    /*These two variables store a local copy of packedQuestion. These variables are first updated with the information from
    onChange (or a variation of it), and then packedQuestion is set to an instance of this. */

    let local_question_mcq = {};
    let local_answerChoices_mcq =  {};

    function fillUpQuestionWithDefault(){

        function addOption(character, value) {
            local_answerChoices_mcq[character] = value;
            local_question_mcq["answer_choices"] = local_answerChoices_mcq;
        }

        function addQuestion(title, value){

            if(title){
                local_question_mcq['title'] = value;
            }
            else {
                local_question_mcq['question_text'] = value;
            }

        }

        addQuestion(true, "Question "+props.id);
        addQuestion(false, "");

        addOption("a", "");
        addOption("b", "");
        addOption("c", "");
        addOption("d", "");

        local_question_mcq['title'] = "Question " + props.id;
        local_question_mcq['id'] = props.id;
        
        setPackedQuestion(local_question_mcq);

    }

    useEffect(() =>fillUpQuestionWithDefault(), []);


    function optionOnInputFunc(character, value) {
        local_question_mcq = {...packedQuestion};
        local_answerChoices_mcq = {...local_question_mcq["answer_choices"]};
        local_answerChoices_mcq[character] = value;
        local_question_mcq["answer_choices"] = local_answerChoices_mcq;
        
        setPackedQuestion(local_question_mcq);

    }

    function questionOnInputFunc(title, value) {

        if(title){
            local_question_mcq = {...packedQuestion};
            local_question_mcq['title'] = value;
            setPackedQuestion(local_question_mcq);
        }

        else {
            local_question_mcq = {...packedQuestion};
            local_question_mcq['question_text'] = value;
            setPackedQuestion(local_question_mcq);
        }
        
    }


    function mcqChoiceGeneratingFunc() {

        return (
            
            <div class = "Opt">
                <TextField onChange = {e => optionOnInputFunc('a', e.target.value)}  label="Option A" variant="filled" multiline rowsMax={4}/>
                <TextField onChange = {e => optionOnInputFunc('b', e.target.value)}  label="Option B" variant="filled" multiline rowsMax={4}/>
                <TextField onChange = {e => optionOnInputFunc('c', e.target.value)}  label="Option C" variant="filled" multiline rowsMax={4}/>
                <TextField onChange = {e => optionOnInputFunc('d', e.target.value)}  label="Option D" variant="filled" multiline rowsMax={4}/>
            </div>

        );
    }



    return (

        <Paper class="Question">
            <form class="Question-form">

                <a class = "editpencil">✎</a>
                <InputBase class = "questionedit"
                    onChange = {e => questionOnInputFunc(true, e.target.value)}
                    defaultValue={"Question "+props.id}
                    inputProps = {{"maxlength": 40}}/>
                

                <div class="question-text">
                    <TextField onChange = {e => questionOnInputFunc(false, e.target.value)} variant="outlined" fullWidth="true" label="Type your question"></TextField>
                </div>
                {mcqChoiceGeneratingFunc()}


            </form>
            
        </Paper>


    );

}

export default McqQuestion;
import./McqQuestion.css'
从“React”导入React,{useState,useffect};
从“@material ui/core”导入{Paper,TextField};
从“@material ui/core/InputBase”导入InputBase;
/*
该组件允许制造者创建问题,然后将问题存储到packedQuestion。
packedQuestipn的格式可以直接发送到API并上传到数据库。
一个基本问题有疑问句文本,疑问句.title
道具通过:
props.id=问题id。
props.returnfunc=完成所有操作后,使用packedQuestion和props.id调用的函数。
returnfunc(props.id,packedQuestion)是被调用的东西。
*/
功能MCQQ问题(道具){
const[packedQuestion,setPackedQuestion]=useState({});
useffect(()=>props.returnfunc(props.id,packedQuestion));
/*这两个变量存储packedQuestion的本地副本。这些变量首先使用来自的信息进行更新
onChange(或其变体),然后将packedQuestion设置为该问题的一个实例*/
让局部问题{mcq={};
让本地_-answerChoices_-mcq={};
函数fillUpQuestionWithDefault(){
函数addOption(字符、值){
本地应答选项mcq[字符]=值;
本地问题mcq[“答案选择”]=本地答案选择mcq;
}
函数addQuestion(标题、值){
如果(标题){
本地问题mcq['title']=值;
}
否则{
本地问题mcq['question\u text']=值;
}
}
addQuestion(true,“问题”+道具id);
添加问题(假“);
添加选项(“a”和“);
添加选项(“b”和“);
添加选项(“c”和“);
添加选项(“d”和“);
本地问题mcq['title']=“问题”+道具id;
本地问题mcq['id']=props.id;
设置打包问题(本地问题);
}
useEffect(()=>fillUpQuestionWithDefault(),[]);
函数选项InputFunc(字符、值){
局部问题(mcq={…packedQuestion};
本地答案选项{…本地问题{mcq[“答案选项”]};
本地应答选项mcq[字符]=值;
本地问题mcq[“答案选择”]=本地答案选择mcq;
设置打包问题(本地问题);
}
函数问题InputFunc(标题、值){
如果(标题){
局部问题(mcq={…packedQuestion};
本地问题mcq['title']=值;
设置打包问题(本地问题);
}
否则{
局部问题(mcq={…packedQuestion};
本地问题mcq['question\u text']=值;
设置打包问题(本地问题);
}
}
函数mcqChoiceGeneratingFunc(){
返回(
optionInInputFunc('a',e.target.value)}label=“Option a”variant=“filled”多行行最大值={4}/>
optionInInputFunc('b',e.target.value)}label=“Option b”variant=“filled”多行行最大值={4}/>
optionInInputFunc('c',e.target.value)}label=“Option c”variant=“filled”多行行最大值={4}/>
optionInInputFunc('d',e.target.value)}label=“Option d”variant=“filled”多行行最大值={4}/>
);
}
返回(


为什么会发生这种情况?我如何修复这种情况?

当您从子组件调用此函数时,ques会在钩子中获取初始状态的值,即{}。现在您正在temp中添加键quesID并更新状态。因此这将是一种预期行为

function returnFromMcqQuestion(prevQues, quesID, thisQuestion) {
            var temp = {...prevQues};
            temp["ques"+quesID] = thisQuestion;
    
            setQues(prevQues);
          }
所以你需要这样的东西

<McqQuestion ques={ques} returnfunc={returnFromMcqQuestion} id={questionid}/>)
useEffect(()=> props.returnfunc(props.ques, props.id, packedQuestion));
)
useffect(()=>props.returnfunc(props.ques、props.id、packedQuestion));
问题 埃克萨马雷亚
  • 以状态存储react组件是一种react反模式和可靠的方法,可以让您自己获得一些过时的状态机柜
  • 只存储状态中的数据并从中呈现UI

  • 无论何时更新依赖于前一状态的react状态(即,向数组追加元素、增加计数/id等),都不会使用功能状态更新
  • 使用“功能状态更新”可以正确地从任何以前的状态更新,而不是从以前的渲染周期更新

    MCQQ问题
  • 一旦我在
    ExamArea
    中解决了您的问题,我就有点被
    local\u question\u mcq
    local\u answerChoices\u mcq
    的使用打乱了。乍一看,他们是
    function ExamArea(props) {
      const [currentQuesID, setCurrentQuesID] = useState(2);
      const [mcqQuestionList, setmcqQuestionList] = useState([{ id: 1 }]); // <-- store data only
    
      function returnFromMcqQuestion(quesID, thisQuestion) {
        setmcqQuestionList((mcqQuestionList) => // <-- functional state update
          mcqQuestionList.map((question) =>
            question.id === quesID // <-- shallow copy matching question
              ? {
                  ...question,
                  ...thisQuestion
                }
              : question
          )
        );
      }
    
      function generateMCQ(questionid) {
        return {
          id: questionid
        };
      }
    
      function addAnotherQuestion() {
        setmcqQuestionList((mcqQuestionList) => // <-- functional state update
          mcqQuestionList.concat(generateMCQ(currentQuesID))
        );
        setCurrentQuesID((c) => c + 1); // <-- functional state update
      }
    
      return (
        <div className="ExamArea">
          {mcqQuestionList.map(({ id }) => (
            <McqQuestion
              key={id}
              returnfunc={returnFromMcqQuestion} // <-- pass callback directly
              id={id}
            />
          ))}
          <button
            onClick={addAnotherQuestion}
            className="add_another_question_button"
          >
            + Add Another Question
          </button>
        </div>
      );
    }
    
    function McqQuestion(props) {
      const [packedQuestion, setPackedQuestion] = useState({});
    
      useEffect(() => {
        props.returnfunc(props.id, packedQuestion); // <-- update state in parent
      }, [packedQuestion]);
    
      function fillUpQuestionWithDefault() {
        /*These two variables store a local copy of packedQuestion. These variables are first updated with the information from
        onChange (or a variation of it), and then packedQuestion is set to an instance of this. */
        const local_question_mcq = { // <-- provide initial values, then override
          id: props.id,
          title: `Question ${props.id}`,
        };
        const local_answerChoices_mcq = {};
    
        function addOption(character, value = '') {
          local_answerChoices_mcq[character] = value;
          local_question_mcq["answer_choices"] = local_answerChoices_mcq;
        }
    
        function addQuestion(title, value) {
          local_question_mcq[title ? "title" : "question_text"] = value; // <-- DRY
        }
    
        addQuestion(true, "Question " + props.id);
        addQuestion(false, "");
    
        ['a', 'b', 'c', 'd'].forEach(c => addOption(c, '')); // <-- DRY
    
        setPackedQuestion(local_question_mcq);
      }
    
      useEffect(() => {
        fillUpQuestionWithDefault();
      }, []);
    
      function optionOnInputFunc(character, value) {
        setPackedQuestion((question) => ({ // <-- functional state update
          ...question,
          answer_choices: {
            ...question.answer_choices,
            [character]: value
          }
        }));
      }
    
      function questionOnInputFunc(title, value) {
        setPackedQuestion((question) => ({ // <-- functional state update
          ...question,
          [title ? 'title' : 'question_text']: value
        }));
      }
    
      function mcqChoiceGeneratingFunc() {
        return (
          <div className="Opt">
            ...
          </div>
        );
      }
    
      return (
        <Paper className="Question">
          ...
        </Paper>
      );
    }