有没有办法在重置所有状态后调用 useEffect 钩子

时间:2021-01-27 16:51:24

标签: reactjs

在我的 App.tsx 文件中,我想暂停所有状态并调用 useEffect 钩子来获取测验数据。当它第一次运行时,它运行得很好,但是当我单击 handleRetake 函数重新进行测验时,它重置了所有状态,但没有调用 useEffect 钩子来调用 API。 这是我的代码

function App() {

  let [quiz,setQuiz] = useState<QuizType[]>([])
  let [currentStep,setCurrentStep] = useState(0)
  let [score,setScore] = useState(0)
  let [showResult, setShowResult] = useState(false)


  

  useEffect(()=>{
    async function fetchData() {
      

      const questions = await getQuizDetails(5,'easy')

      setQuiz(questions)
      
    }
    fetchData();
  },[])

  const handleRetake = (event:React.FormEvent<EventTarget>) => {
    event.preventDefault();
    setCurrentStep(0)
    setQuiz([])
    setShowResult(false)
    setScore(0)
}
  
  

  if(!quiz.length)
    return <Container>
        <h1>Loading ..... </h1>
    </Container>
  
  if(showResult){
    return(
      <Container>
        <Row>
          <Col md={{span:6 , offset:3}}>
            <Card>
              <Card.Header>
                Result
              </Card.Header>
              <Card.Body>
                <h1>Your Score is {score} out of {quiz.length}</h1>
                <Button onClick={handleRetake}> Retake Quiz</Button>
              </Card.Body>
            </Card>
          </Col>
        </Row>
      </Container>
    )
  }

2 个答案:

答案 0 :(得分:1)

您可以做的一件事是将 quiz 添加到 useEffect 的依赖项列表并添加检查,以便每当 quiz 为空列表时,重新获取它。

  useEffect(()=>{
   if(quiz.length === 0) {
    async function fetchData() {
      const questions = await getQuizDetails(5,'easy')
      setQuiz(questions)
    }
   fetchData();
   }
  },[quiz])

此外,这只是满足您要求的方法之一。实际上,您也可以在 fetchData 之外声明 useEffect 以在需要时重用它。同样使用此方法,您必须确保您的 getQuizDetails 返回一个非空列表,否则您将陷入一次又一次获取列表的循环中。

答案 1 :(得分:0)

@lakshya-thakur 在最后的回答中暗示了这一点,但我觉得这非常重要。

根据我目前所学/经验,我们应该避免在状态更新本身时触发改变状态变量值的 API/函数调用。相反,消除 API/函数调用对状态更新的依赖,而是在外部/基于用户的事件(初始页面呈现 - useEffect(()=>{},[]) 和/或按钮点击)上触发它们将防止不必要的组件重新呈现并提供更好、更强大的解决方案。这也消除了 useEffect 内部不必要的检查,这些检查是决定状态是否需要更新或 API/函数调用是否也需要进行的必要检查。

在下面的代码中,initQuizData 函数在初始组件渲染时被调用,并且在点击 retake quiz 按钮时被调用。如您所见,API/函数调用 getQuizDetails 不再依赖于状态变量 quiz 的值,而是依赖于实际打算进行该 API/函数调用的外部因素。

相反,每次更新 fetchData 状态时都会调用 quiz(我同意在这种情况下只是在测验之前和测验之后一次,但遵循相同的模式可能会结束对频繁更改的状态执行此操作),此处 initQuizData 仅在需要时才被调用,因此在其中不需要进行条件检查。

function App() {

  let [quiz,setQuiz] = useState<QuizType[]>([]);
  let [currentStep,setCurrentStep] = useState(0);
  let [score,setScore] = useState(0);
  let [showResult, setShowResult] = useState(false);

  useEffect(()=>{
    initQuizData();
  },[]);

  const initQuizData = async () => {
    event.preventDefault();
    setCurrentStep(0);
    setQuiz([]);
    setShowResult(false);
    setScore(0);
    const questions = await getQuizDetails(5,'easy');
    setQuiz(questions);
  };
  
  if(!quiz.length)
    return <Container>
        <h1>Loading ..... </h1>
    </Container>
  
  if(showResult){
    return(
      <Container>
        <Row>
          <Col md={{span:6 , offset:3}}>
            <Card>
              <Card.Header>
                Result
              </Card.Header>
              <Card.Body>
                <h1>Your Score is {score} out of {quiz.length}</h1>
                <Button onClick={initQuizData}> Retake Quiz</Button>
              </Card.Body>
            </Card>
          </Col>
        </Row>
      </Container>
    );
  }

上面的解决方法很粗糙,我没有亲自测试过。我只是想传达我上面所说的想法,作为处理此类情况的方法。