reactjs - 一个 useState() 实例覆盖 React App 中的另一个实例
问题描述
我的 React/Ionic 应用程序中有一个功能组件,我需要在其中跟踪两件事的状态:1.) 用户从多项选择题系列中做出的最终选择。并且 2.) 当用户为每个问题做出选择时,此时我根据questionsAnswered
属性的布尔状态有条件地显示我的“提交答案”按钮 - 使用我的useState()
.
我遇到的问题是,即使它们被定义为两个单独的实例useState()
,一个会覆盖另一个。具体来说,当用户为每个问题选择了一个选项时,我的提交按钮按预期显示,但是当基于这些选择生成的分数传递给下一个组件时,它被传递为 0 - 建议useState()
已将所有内容重置为默认值状态。
需要明确的是,当我没有为三个有答案的问题设置状态时 - 换句话说,如果我只是让“提交答案”按钮可见,并且不使用在我的第一个useState()
实例中处理的条件隐藏它,那么正确的score
,在我的第二个实例中处理useState()
,被转发。
我不确定我在这里缺少什么。此外,如果有不同的方式来处理有条件的处理显示我的“提交答案”按钮,我愿意听到它。但无论哪种方式,我都不确定如何使用两个这样的实例useState()
,而不会相互干扰。
这是我的完整组件,使用了一些模拟数据:
import {
IonButtons,
IonContent,
IonHeader,
IonMenuButton,
IonPage,
IonTitle,
IonToolbar,
IonRadioGroup,
IonList,
IonLabel,
IonListHeader,
IonItem,
IonRadio,
IonItemDivider,
IonButton,
IonCard,
IonCardContent,
} from "@ionic/react";
import React, { useState } from "react";
import axios from "axios";
import { useHistory } from "react-router";
import { APIConfig } from "./../../../environments/environment";
const Questions: React.FC = () => {
let [questionsAnswered, setAnswered] = useState(false);
let [locQuestions, setAnswers] = useState<locQuestions[]>([]);
let score = 0;
const history = useHistory();
let questions = [
{
id: 1,
question: "What is the closest planet to the sun?",
choices: ["Mercury", "Mars", "Venus", "Earth"],
answer: "Mercury",
choice: "",
},
{
id: 2,
question: "What is known as 'the Red Planet'?",
choices: ["Jupiter", "Mars", "Venus", "Saturn"],
answer: "Mars",
choice: "",
},
{
id: 3,
question: "What planet has the 'Giant Red Spot'?",
choices: ["Neptune", "Venus", "Jupiter", "Mars"],
answer: "Jupiter",
choice: "",
},
];
const getChoice = (qId: any, qChoice: any) => {
locQuestions = [...questions];
for (let loc of locQuestions) {
if (loc.id === qId) {
loc.choice = qChoice;
}
}
checkIfAnswersComplete();
};
const checkIfAnswersComplete = () => {
let answeredArr = locQuestions.filter((i) => i.choice !== "");
if (answeredArr.length === locQuestions.length) {
console.log("All questions answered!!!");
setAnswered(true); // Set to true here, because all questions are answered
}
};
const getScore = () => {
setAnswers(locQuestions); // State for the final choices made by user
for (const q of locQuestions) {
console.log("locQuestions in getScore(): ", q);
if (q.answer === q.choice) {
score++;
}
}
return score;
};
const submitAnswers = async () => {
getScore();
await updateUser();
history.push("/score", score); // When score gets sent it's now 0
};
const updateUser = async (
username = "tester",
lastCompletedSet = "1",
lastCompletedTopic = "starter",
pointsToAdd = score
) => {
let result: object;
const req = `users/update-user-stats`;
const body = {
username,
lastCompletedSet,
lastCompletedTopic,
pointsToAdd,
};
result = await axios.post(`${APIConfig.url}/${req}`, body);
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonMenuButton />
</IonButtons>
<IonTitle>Questions</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonCard>
<IonCardContent>
<IonList>
{questions.map((q, questionIndex) => (
<IonRadioGroup key={q.id}>
<IonListHeader>
<IonLabel class="question">{q?.question}</IonLabel>
</IonListHeader>
{q.choices.map((choice, optionIndex) => (
<IonItem key={choice[optionIndex]}>
<IonLabel>{choice}</IonLabel>
<IonRadio
slot="start"
value={choice}
onClick={() => getChoice(q.id, choice)}
></IonRadio>
</IonItem>
))}
</IonRadioGroup>
))}
</IonList>
<IonItemDivider className="divider"></IonItemDivider>
{questionsAnswered ? (
<IonButton expand="block" onClick={() => submitAnswers()}>
SUBMIT ANSWERS
</IonButton>
) : null}
</IonCardContent>
</IonCard>
</IonContent>
</IonPage>
);
};
export default Questions;
解决方案
在这一行:
locQuestions = [...questions];
您正在直接改变状态。你不应该那样做。相反,您应该locQuestion
通过调用setLocQuestions
新数组来更新。
我创建了一个代码框来展示它是如何工作的。
也在这里发布:
const [locQuestions, setQuestions] = useState(questions);
const [answers, setAnswers] = useState([]);
const areAllQuestionsAnswered =
answers.filter(Boolean).length === locQuestions.length;
const getChoice = (qId, qChoice) => {
setAnswers((currentAnswers) => {
const result = [...currentAnswers];
result[qId] = qChoice;
return result;
});
};
const getScore = () => {
let score = 0;
locQuestions.forEach((q) => {
if (q.answer === answers[q.id]) {
score++;
}
});
return score;
};
const submitAnswers = async () => {
getScore();
console.log("score is:", getScore());
// await updateUser();
};
推荐阅读
- python - Pandas 透视数据框并根据它们是否存在将新列设置为 True/False
- python - 如何在另一个列表中的项目之间插入列表中的项目?
- r - 比较一个数据帧中的两对列以检测不匹配并在同一行中显示另一列的值
- java - 有什么方法可以遍历由 java 中的生成器创建的随机 json 字符串?
- asp.net-core - Keycloak .net 5 api 服务
- ruby - Ruby 无法断言引发的异常
- xml - 当 Jmeter 提交带有 xml 数据的 POST 时,应用程序返回 500 错误
- java - MongoRepository 防止递归 dbref
- css - Vue2 | DIV 在 v-for 上不改变高度
- r - 在 R 中为 ggtree 添加注释