首页 > 解决方案 > 一个 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;

标签: reactjsionic-framework

解决方案


在这一行:

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();
  };

编辑 beautiful-drake-t7vzq


推荐阅读