我的按钮需要点击两次才能呈现新组件,第一次点击React

3
我正在处理一个从开放的Trivia API获取数据的项目。我已经开发好了一切,目前看起来一切都运行良好。然而,当我首次渲染应用程序时,第一次点击提交答案按钮时,它调用函数,但没有呈现新的问题和答案选项...为了呈现,我必须再次点击选择答案并再次单击按钮。在点击检查答案按钮第二次渲染后,其余应用程序在第一次点击时运行得非常完美。我不确定是什么原因导致这个错误,你能帮帮我吗? 这是我的代码:
  import React, { useEffect, useState } from "react";
import { Container, Row, Card, Button, Alert } from "react-bootstrap";
import axios from "axios";
import "../App.css";
import Results from "./Results";

export const createMarkup = (text) => {
  return { __html: text };
};
const QuizCategory = () => {
  const [quizData, setquizData] = useState([]);
  const [category, setCategory] = useState([]);
  const [question, setQuestion] = useState([]);
  const [options, setOptions] = useState([]);
  const [questionsData, setQuestionsData] = useState([]);
  const [questionNumber, setQuestionNumber] = useState(0);
  const [selectedAnswer, setSelectedAnswer] = useState();
  const [correctAnswer, setCorrectAnswer] = useState();
  const [score, setScore] = useState(0);
  const [prevSelectedElement, setPrevSelectedElement] = useState();
  const [quizState, setQuizState] = useState("start");
  const [showAlert, setShowAlert] = useState(false);
  const [rightAlert, setRightAlert] = useState(false);
  const [wrongAlert, setWrongAlert] = useState(false);
  const totalQuestion = 10;

  const fetchQuizData = async () => {
    const { data } = await axios.get("https://opentdb.com/api.php?amount=10");
    setQuestionsData(data);
    console.log(data);
    setCategory(data.results[questionNumber].category);
    setQuestion(data.results[questionNumber].question);
    setCorrectAnswer(data.results[questionNumber].correct_answer);

    const formattedData = data.results.map((category) => {
      const incorrectAnswersIndexes = category.incorrect_answers.length;
      const randomIndexes = Math.random() * (incorrectAnswersIndexes - 0) + 0;
      category.incorrect_answers.splice(
        randomIndexes,
        0,
        category.correct_answer
      );
      return {
        ...category,
        answers: category.incorrect_answers,
      };
    });

    setquizData(formattedData);
    setOptions(formattedData[questionNumber].answers);
  };

  useEffect(() => {
    fetchQuizData();
  }, []);
  const selectAnswer = (e, answer) => {
    setSelectedAnswer(answer);
    console.log("Answer Selected:", answer);

    const optionSelected = "options-selected";
    const newSelectedElement = e.target;

    if (newSelectedElement.classList.value === optionSelected) {
      newSelectedElement.classList.replace("options-selected", "options");
      setPrevSelectedElement(newSelectedElement);
    } else if (prevSelectedElement) {
      prevSelectedElement.classList.replace("options-selected", "options");
      newSelectedElement.classList.replace("options", "options-selected");
      setPrevSelectedElement(newSelectedElement);
    } else {
      newSelectedElement.classList.replace("options", "options-selected");
      setPrevSelectedElement(newSelectedElement);
    }
  };
  const checkAnswer = () => {
    console.log(
      selectedAnswer,
      correctAnswer,
      score,
      questionNumber,
      question,
      category,
      options
    );
    if (selectedAnswer === correctAnswer && questionNumber < 10) {
      setScore(score + 1);
      setQuestionNumber(questionNumber + 1);
      setQuestion(questionsData.results[questionNumber].question);
      setCategory(questionsData.results[questionNumber].category);
      setOptions(quizData[questionNumber].answers);
      setSelectedAnswer();
      setCorrectAnswer(questionsData.results[questionNumber].correct_answer);
      setShowAlert(false);

      console.log("Correct", questionNumber);
    } else if (
      selectedAnswer !== undefined &&
      selectedAnswer !== correctAnswer &&
      questionNumber < 10
    ) {
      setQuestionNumber(questionNumber + 1);
      setQuestion(questionsData.results[questionNumber].question);
      setCategory(questionsData.results[questionNumber].category);
      setOptions(quizData[questionNumber].answers);
      setSelectedAnswer();
      setCorrectAnswer(questionsData.results[questionNumber].correct_answer);
      setShowAlert(false);

      console.log("Incorrect", questionNumber);
    } else if (
      selectedAnswer !== undefined &&
      selectedAnswer === correctAnswer &&
      questionNumber === 10
    ) {
      console.log("Ready to start again?");
      setScore(score + 1);
      setQuizState("results");
      setShowAlert(false);
    } else if (
      selectedAnswer !== undefined &&
      selectedAnswer !== correctAnswer &&
      questionNumber === 10
    ) {
      console.log("Ready to start again?");
      setShowAlert(false);
      setQuizState("results");
    } else {
      setShowAlert(true);
    }
    console.log(
      selectedAnswer,
      correctAnswer,
      score,
      questionNumber,
      question,
      category
    );
  };
  console.log(selectedAnswer);
  return (
    <div>
      <Container>
        <div className="title-text">
          <h1 className="trivia-text">Trivia</h1>
          <h1 className="coin-text"> Coin</h1>
          <h1 className="game-text">Game</h1>
        </div>

        <Row>
          {quizState === "start" ? (
            <>
              <Card className="card-big">
                <Card.Title>
                  <div
                    className="category"
                    dangerouslySetInnerHTML={createMarkup(category)}
                  ></div>
                </Card.Title>
                <Card.Body>
                  <div className="score">
                    <p
                      dangerouslySetInnerHTML={createMarkup(score)}
                      className="correct-score"
                    ></p>
                    /
                    <p
                      className="total-question"
                      dangerouslySetInnerHTML={createMarkup(totalQuestion)}
                    ></p>
                  </div>
                  <div
                    className="question"
                    dangerouslySetInnerHTML={createMarkup(question)}
                  ></div>
                  {options.map((answer) => (
                    <div
                      onClick={(e) => selectAnswer(e, answer)}
                      key={answer}
                      value={answer}
                    >
                      <li
                        className="options"
                        dangerouslySetInnerHTML={createMarkup(answer)}
                      ></li>
                    </div>
                  ))}

                  {showAlert === true ? (
                    <>
                      <Alert
                        variant="light"
                        onClose={() => setShowAlert(false)}
                        dismissible
                      >
                        <Alert.Heading>Please choose an answer!</Alert.Heading>
                      </Alert>
                    </>
                  ) : (
                    <div className="alert-component"></div>
                  )}
                  {rightAlert === true ? (
                    <>
                      <Alert
                        variant="success"
                        onClose={() => setShowAlert(false)}
                        dismissible
                      >
                        <Alert.Heading>That's Right, Good Job!</Alert.Heading>
                      </Alert>
                    </>
                  ) : (
                    <div className="alert-component"></div>
                  )}
                  {wrongAlert === true ? (
                    <>
                      <Alert
                        variant="danger"
                        onClose={() => setShowAlert(false)}
                        dismissible
                      >
                        <Alert.Heading>
                          The Right Answer is <b>{correctAnswer}</b>. You Got
                          the Next One!
                        </Alert.Heading>
                      </Alert>
                    </>
                  ) : (
                    <div className="alert-component"></div>
                  )}
                  <Button
                    className="check-btn"
                    size="large"
                    variant="outline-light"
                    onClick={() => checkAnswer()}
                  >
                    CHECK ANSWER
                  </Button>
                </Card.Body>
              </Card>
            </>
          ) : (
            <Results
              createMarkup={createMarkup}
              score={score}
              totalQuestion={totalQuestion}
            />
          )}
        </Row>
      </Container>
    </div>
  );
};

export default QuizCategory;

2
这是相当多的代码,你应该能够使用一个小例子来重现你的问题,并将其作为 [mre] 共享。第一眼看起来,当你调用 .splice() 时,你正在改变你的状态。 - Nick Parsons
好的,存在问题的按钮是“检查答案”按钮。正如我所说,它只会在我提取第一个问题时出现。我想知道是否因为状态没有立即更新... - Enrique Alcacer
1个回答

1
问题: 当您执行以下两行代码时,不能期望在运行第二行代码时questionNumber已经增加了一。
setQuestionNumber(questionNumber + 1);
setQuestion(questionsData.results[questionNumber].question);
解决方案1: 您可以使用useEffect监听问题编号的变化,并相应地采取行动。
  useEffect(() => {
    if (questionNumber >= 1 && questionNumber < 10) {
      setQuestion(questionsData.results[questionNumber].question);
      setCategory(questionsData.results[questionNumber].category);
      setOptions(quizData[questionNumber].answers);
      setSelectedAnswer();
      setCorrectAnswer(questionsData.results[questionNumber].correct_answer);
      setShowAlert(false);
    }
  }, [questionNumber]);
只需将更新 quesionNumber 的逻辑保留在 if-else 块中,每当它更新 useEffect 都会以递增的值触发。

Edit boring-cloud-boy8n5

解决方案2: 您可以按照以下方式在连续的行中递增“questionNumber”(使用“questionNumber + 1”而不是仅使用“questionNumber”)。
setQuestionNumber(questionNumber + 1);
setQuestion(questionsData.results[questionNumber + 1].question);
setCategory(questionsData.results[questionNumber + 1].category);
setOptions(quizData[questionNumber + 1].answers);
setSelectedAnswer();
setCorrectAnswer(questionsData.results[questionNumber + 1].correct_answer);
setShowAlert(false);

Edit friendly-firefly-n2qdog

你需要在 if 和 else if 块中都这样做。

@Enrique Alcacer,看这个!! - Amila Senadheera
嗨@Amila Senadheera!这太棒了。使用effect修复了按钮的问题,但是因为我正在使用useEffect获取数据,所以当我按下按钮时,数据会再次检索新数据,因此所有数据都不同步。您知道任何可能的解决方案吗?非常感谢您的帮助! - Enrique Alcacer
是的,我将它们合并在同一个组件中了... - Enrique Alcacer
@Enrique Alcacer,那就是问题所在了。如果两者的依赖数组相同,那么您可以将它们合并,但实际上不是这样(其中一个为空,另一个有questionNumber)。现在每当questionNumber增加时,它都会获取一组新数据。 - Amila Senadheera
我已经解决了。我使用了你的第一种方法,但是我使用了useMemo来渲染只有那一部分。感谢你的帮助!@Amila Senadheera - Enrique Alcacer
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,