Javascript 嵌套对象存储为状态变量,在修改旧值时删除新值
我有一个react组件,它将用户输入值传递给props函数。父函数只是将这些输入附加到对象。但是,当修改旧值时,将删除所有新值。请参考屏幕截图 这是家长。ExamArea.jsJavascript 嵌套对象存储为状态变量,在修改旧值时删除新值,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
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>
);
}