React limits the number of renders to prevent an infinite loop error, while using empty array dependencies in useEffect()

Mike Ouroumis

I am trying to render some fetched data to the DOM from an API which is called in useEffect() hook. I set a boolean "isFetching" to start rendering the data after the fetch process ends, but I get an error for infinite loop instead. The only way that my fetched data appears on render is while I use setTimeout() and wait for the data to be fetched, but this is only for testing...

import "bootstrap/dist/css/bootstrap.css";
import { useEffect } from "react";
import axios from "axios";

export default function App() {
  //the array of loadedQuestions that is fetched from the API
  let [loadedQuestions, setLoadedQuestions] = React.useState([]);
  let [answers, setAnswers] = React.useState([]);
  let [isFetching, setIsFetching] = React.useState(true);

  async function fetchData() {
    const response = await axios.get(
      "https://opentdb.com/api.php?amount=10&category=18&difficulty=easy&type=multiple"
    );
    console.log(response);
    const data = await response.data;
    loadedQuestions = data.results;
    setLoadedQuestions(loadedQuestions);

    //adding the correct answer to incorrect_answers array
    for (let obj of loadedQuestions)
      obj.incorrect_answers.splice(
        Math.floor(Math.random() * 4),
        0,
        obj.correct_answer
      );
    setIsFetching(false);
    isFetching = false;
    console.log(isFetching);
  }

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

  const [isClicked, setIsClicked] = React.useState(false);
  const [currentQuestion, setCurrentQuestion] = React.useState(0);
  const [score, setScore] = React.useState(0);
  const [finished, setFinished] = React.useState(false);
  const [beforeFinish, setBeforeFinish] = React.useState("");

  //setTimeout only for testing....
  // setTimeout(() => {
  if (!isFetching) {
    setAnswers(
      loadedQuestions[currentQuestion].incorrect_answers.map((element) => (
        <li>
          <span
            key={currentQuestion.toString()}
            onClick={() => handleClick(element)}
            style={{ cursor: "pointer" }}
            className={isClicked ? textColor(element) : "bg"}
          >
            {element}
          </span>
        </li>
      ))
    );

    setBeforeFinish(
      <div>
        <h5 className="m-3" style={{ textDecoration: "underline" }}>
          {loadedQuestions[currentQuestion].question}
        </h5>
        <ol style={{ listStyleType: "lower-latin" }}>{answers}</ol>
        <button
          onClick={() => nextQuestionFunction()}
          style={{
            backgroundColor: "#0c88fb",
            color: "white",
            marginLeft: 10,
          }}
        >
          Next Question
        </button>
        <h5 style={{ marginTop: 15, marginLeft: 10 }}>
          Your score is{" "}
          <span style={{ color: "#0c88fb", fontWeight: "bold" }}>{score}</span>!
        </h5>
      </div>
    );
  }
  // }, 2000);

  const afterFinish = (
    <div>
      <h1>Finished!</h1>
      <h5>
        Your Score is{" "}
        <span style={{ color: "#0c88fb", fontWeight: "bold" }}>{score}</span>
      </h5>
      <button
        onClick={() => tryAgain()}
        style={{ backgroundColor: "#0c88fb", color: "white", marginLeft: 2 }}
      >
        Try Again
      </button>
    </div>
  );

  function handleClick(element) {
    setIsClicked(true);
    textColor(element);

    if (element === loadedQuestions[currentQuestion].correct_answer) {
      setScore(score + 100 / loadedQuestions.length);
    }
  }

  function textColor(element) {
    let classN = "bg ";
    element === loadedQuestions[currentQuestion].correct_answer
      ? (classN += "bg-info")
      : (classN += "bg-secondary");

    return classN;
  }

  function nextQuestionFunction() {
    if (currentQuestion + 1 === loadedQuestions.length) {
      setFinished(true);
    } else {
      setCurrentQuestion(currentQuestion + 1);
      setIsClicked(false);
    }
  }

  function textDisplay() {
    if (finished) {
      return afterFinish;
    } else {
      return beforeFinish;
    }
  }

  function tryAgain() {
    setCurrentQuestion(0);
    setScore(0);
    setIsClicked(false);
    setFinished(false);
  }

  return textDisplay();
}
Nicholas Tower
if (!isFetching) {
  setAnswers(
    loadedQuestions[currentQuestion].incorrect_answers.map((element) => (
      // etc
    ))
  );

  setBeforeFinish(
    <div>
      // etc
    </div>
  );
}

You are calling setAnswers and setBeforeFinish in the middle of rendering. Setting state causes the component to rerender and when it renders you set state again, which renders again, which sets state again, etc.

These don't look like they should even be states at all. loadedQuestions and isFetching are the true state, and then answers and beforeFinish are just values calculated from that state. I recommend you delete the answers and beforeFinish states, and where you used to have the if (!isFetching) code you do:

let answers = null;
let beforeFinish = null;
if (!isFetching) {
  answers = loadedQuestions[currentQuestion].incorrect_answers.map(
    (element) => (
      <li>
        <span
          key={currentQuestion.toString()}
          onClick={() => handleClick(element)}
          style={{ cursor: "pointer" }}
          className={isClicked ? textColor(element) : "bg"}
        >
          {element}
        </span>
      </li>
    )
  );
  beforeFinish = (
    <div>
      <h5 className="m-3" style={{ textDecoration: "underline" }}>
        {loadedQuestions[currentQuestion].question}
      </h5>
      <ol style={{ listStyleType: "lower-latin" }}>{answers}</ol>
      <button
        onClick={() => nextQuestionFunction()}
        style={{
          backgroundColor: "#0c88fb",
          color: "white",
          marginLeft: 10,
        }}
      >
        Next Question
      </button>
      <h5 style={{ marginTop: 15, marginLeft: 10 }}>
        Your score is{" "}
        <span style={{ color: "#0c88fb", fontWeight: "bold" }}>{score}</span>!
      </h5>
    </div>
  );
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

"Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."

error too many re-renders. react limits the number of renders to prevent an infinite loop

received this error "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."

ReactJS, react-bootstrap, Modal Box: "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop and it disappear when i clear the stored session data

React: setting a state Hook causes error - Too many re-renders. React limits the number of renders to prevent an infinite loop

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. Unable to find code which is causing it

React giving me Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

why do i get Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

Too many re-renders. React limits the number of renders to prevent an infinite loop-error

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop React Native

useState and if statement causing Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

Uncaught Error:React limits the number of renders to prevent an infinite loop ( ReactAnimatedWeather )

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop when using useState()

useState Array. ERROR Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

Error: "Too many re-renders. React limits the number of renders to prevent an infinite loop"

React.useRef([]) : Error React limits the number of renders to prevent an infinite loop

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop [another variant]

react usestate Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

React limits the number of renders to prevent an infinite loop although I added empty array

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop - ProtectedRoutes Component

Too many re-renders. React limits the number of renders to prevent an infinite loop. Next js error

React : Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

"Too many re-renders. React limits the number of renders to prevent an infinite loop" error when useState hook is used

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. (React-js)

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. (Next-js) Toggle Component

Too many re-renders. React limits the number of renders to prevent an infinite loop. with for loop

Too many re-renders. React limits the number of renders to prevent an infinite loop. when i am using custom hooks