代码之家  ›  专栏  ›  技术社区  ›  Hi - P

useEffect获取数据,即使依赖数组中的状态似乎没有改变

  •  0
  • Hi - P  · 技术社区  · 2 年前

    我正在创建一个测试页面,该页面使用 useEffect 钩提取的数据包括问题和选项,这些选项是多选的。我有一个 Check Answer 按钮,单击时显示用户的最终分数。我还有一个 Play Again 按钮,该按钮在呈现用户的分数后显示。

    当用户点击 再次播放 按钮,本应获取另一个测验数据,但当 检查答案 按钮被按下。我的意思是,当用户点击 检查答案 按钮,它会显示分数(好),但随后整个页面会过早地重新呈现,因此用户看不到他们错了和对了的选项。单击 再次播放 按钮还重新呈现测试页面,这是预期的行为。我有 playAgain state设置为useEffect的依赖数组,以便在状态更改时可以重新呈现测验。但我认为这就是问题的原因。

    另一件事——不知道我是否应该把它作为一个不同的问题来问,但我会把它包括在这里——是当用户点击每个问题的正确选项时,选项会被重新排列。这可能不是什么大不了的事,但我认为我需要修复它来提高用户体验。我猜这和 checkEachAnswer 功能,但我不知道它出了什么问题。

    任何可能出错的想法。谢谢

    下面测验组件的代码。

    
    export default function Quiz() {
    
      const [quiz, setQuiz] = useState(null);
      const [playAgain, setPlayAgain] = useState(false);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(false);
      const [userAnswer, setUserAnswer] = useState(() => []);
      const [showAnswer, setShowAnswer] = useState(false);
      const [showAnswerBtn, setShowAnswerBtn] = useState(true);
       
    
      // This hook fetches data once
      // Added error handling to prevent errors filling up the UI
      useEffect(() => {
        fetch("https://opentdb.com/api.php?amount=5&category=18&difficulty=hard&type=multiple")
          .then(result => {
            if (!result.ok) {
              throw new Error("This is an HTTP error", result.status);
            }
            else {
              return result.json();
            }
          })
          .then(data => {
              setQuiz(data.results);
              console.log("Quiz data stored", data);
              console.log(quiz);
              return quiz && console.log("This is quiz data", quiz);
          })
          .catch(error => {
            console.error("An error occurred!", error);
            setQuiz(null);
            setError(true);
          })
          .finally(() => {
            setLoading(false);
          });
        
      }, [playAgain])
    
    
      const quizElements = quiz && quiz.map(eachQuiz => {
    
        const incorrectOptions = eachQuiz.incorrect_answers;
        const correctOption = quiz && eachQuiz.correct_answer;
        let options = [];
        options = incorrectOptions.concat(correctOption);
    
        function createRandomOptions(arr) {
          
          let copyOptions = [...arr];
          let randomOptionsArr = [];
    
          while (copyOptions.length > 0) {
            let randomIndex = Math.floor(Math.random() * copyOptions.length);
            randomOptionsArr.push(copyOptions[randomIndex]);
            copyOptions.splice(randomIndex, 1); 
          }
    
          return randomOptionsArr;
        }
    
        let randomOptions = createRandomOptions(options);
    
        return (
          <>
            <div className="quiz-wrapper">
              <p className="question">{eachQuiz.question}</p>
              <ul>
                {quiz && randomOptions.map((option, index) => 
                  {
                    // Checks if the clicked option is the correct one and also checks if it was already picked before and prevents it from being added to the userAnswer array
                    function checkEachAnswer() {
                      if (option === correctOption) {
                        console.log("Correct");
    
                        if (userAnswer.includes(option)) {
                          let userAnsArrCopy = [...userAnswer];
                          let index = userAnsArrCopy.findIndex(elem => elem);
                          userAnsArrCopy[index] = option;
                          
                          setUserAnswer(prevValue => {
                            return userAnsArrCopy;
                          }); 
                        }
    
                        else {
                          setUserAnswer(prevValue => {
                          return [...prevValue, option];
                          });
                        }
    
                      }
                      else {
                        console.log(option, "Is incorrect", );
                      }
                    }
              
                    return (
                      <li 
                        key={option} 
                        className="option"
                        onClick={() => checkEachAnswer()}
                      >
                        {option}
                      </li>
                    )
                  })
                }
              </ul>
            </div>
          </>
        )
      });
    
      console.log(userAnswer);
    
      function displayAnswer() {
        setShowAnswer(true);
        setPlayAgain(true);
        setShowAnswerBtn(false);
      }
    
      function updatePlayAgain() {
        setPlayAgain(false);
        setShowAnswer(false);
        setShowAnswerBtn(true);
        setUserAnswer([]);
      }
    
      return (
        <>
          {loading && <h3>Currently loading...</h3>}
          {error && <h3>An error occurred while fetching data!</h3>}
          {quiz && <h1 className="topic">Topic: Computer Science</h1>}
          
          {quiz && quizElements}
    
          {showAnswer && <p>You scored {userAnswer.length} / {quiz.length}</p>}
    
          {quiz && showAnswerBtn && 
            <button 
              onClick={() => displayAnswer()}
              className="main-btn"
            >
              Check Answer
            </button>
          }
    
          {quiz && playAgain && 
            <button 
              onClick={() => updatePlayAgain()}
              className="main-btn"
            >
              Play Again
            </button>
          }
        </>
      )
    }
    
    
    

    如果问题不是我的测验组件,我也会包括应用程序组件。

    import { useState } from "react";
    import Quiz from "./components/Quiz";
    
    const quizId = [
      {"id": 0},
      {"id": 1},
      {"id": 2},
      {"id": 3},
      {"id": 4},
    ]
    
    export default function App() {
      
      const [startQuiz, setStartQuiz] = useState(false);
    
      function loadQuiz() {
        console.log(startQuiz);
        setStartQuiz(true);
      }
    
    
      return (
        <>
          {!startQuiz && 
            <div className="intro-container">
            
              <div className="intro-screen">
                <h1>Quiz My Heart</h1>
                <button className="start-btn" onClick={loadQuiz}>
                  Go to Quiz
                </button>
              </div>
            
            </div>}
    
          {startQuiz && 
            <main> 
              <Quiz key={quizId.map(myId => myId.id)}/>
            </main>
          }
        </>
      );
    }
    
    
    1 回复  |  直到 2 年前
        1
  •  1
  •   Felizj    2 年前

    在您的displayAnswer函数中,当您单击“检查答案”按钮时会调用该函数。你将play再次设置为true,改变它,使你的useEffect生效。您可以添加另一个状态(布尔值),只需切换即可调用useEffect。您只能在updatePlayAgain函数中更新重置

    useEffect只会在依赖数组中重置