Search code examples
reactjsperformanceasynchronousreact-hooksuse-effect

UseEffect mutiple re-renders after async api call and make changes in UI after 1 sec of first call


I'm making a Quiz app, using API from (Trivia api), Issues is - As soon as the api call is made the state is changes 3 times and my UI data changes 2 times in 1 second. I think the issue is related to useEffect even though i'm adding empty dependency in useEffect. can anybody explain why is it happening?

Layout.js

import { useEffect, useState } from 'react'
import { Outlet } from 'react-router-dom'
import Header from '../Componenets/Header/Header'
import ProgressBar from '../Componenets/ProgressBar/ProgressBar'
import QuizMain from '../Componenets/QuizMain/QuizMain'

function Layout() {
  const [questionAre, setQuestionsAre] = useState([])
  const [currentQuestion, setCurrentQuestion] = useState(0)

  const changeCurrentQuestion = (value) => {
    setCurrentQuestion(value)
  }

  useEffect(() => {
    const QuizFetch = async () => {
      try {
        const res = await fetch(
          'https://the-trivia-api.com/api/questions?categories=food_and_drink,general_knowledge&limit=10&region=AU&difficulty=easy',
        )
        const data = await res.json()

        const transformData = data.map((item) => {
          const newarray = item.incorrectAnswers

          return {
            options: newarray,
            question: item.question,
            correctAnswer: item.correctAnswer,
          }
        })

        setQuestionsAre(transformData)
      } catch (err) {
        console.log(err, 'err in getting data')
      }
    }

    QuizFetch()
  }, [])

  return (
    <div className="Layout">
      <Header />
      <ProgressBar
        changeCurrentQuestion={changeCurrentQuestion}
        currentQuestion={currentQuestion}
        questionAre={questionAre}
      />
      {/* <QuizMain
            changeCurrentQuestion={changeCurrentQuestion}
            currentQuestion={currentQuestion}
            questionAre={questionAre} /> */}

      <Outlet context={[changeCurrentQuestion, currentQuestion, questionAre]} />
    </div>
  )
}

export default Layout

Solution

  • Since react 18 and the lifecycle in dev mode you have to use the abortController. The signal will jump to the catch and then you will only have one successfull api call

    useEffect(() => {
        const abortController = new AbortController();
    
        const QuizFetch = async () => {
          
          try {
            const res = await fetch(
              'https://the-trivia-api.com/api/questions?categories=food_and_drink,general_knowledge&limit=10&region=AU&difficulty=easy',
              {
                signal: abortController.signal,
              },
            )
            const data = await res.json()
    
            const transformData = data.map((item) => {
              const newarray = item.incorrectAnswers
    
              return {
                options: newarray,
                question: item.question,
                correctAnswer: item.correctAnswer,
              }
            })
    
            setQuestionsAre(transformData)
          } catch (err) {
            if (abortController.signal.aborted) return;
            console.log(err, 'err in getting data')
          }
        }
    
        QuizFetch()
        
         return () => {
          abortController.abort();
        };
      }, [])