Home > Back-end >  Adding recommended dependency by linter results in an infinite re-rendering loop
Adding recommended dependency by linter results in an infinite re-rendering loop

Time:11-22

Here's my code:

SectionState.js:

import { React, useState, useEffect } from "react";
import QuestionContext from "./QuestionContext";
import questions from "../data/questions.json";
import { useNavigate } from "react-router-dom";

const SectionState = (props) => {

    // set questions from json to an array of 4 elements
    const newQuestions = Object.keys(questions.content).map(
        (key) => questions.content[key].question
    );

    //useState for Question state
    const [currentQuestion, setCurrentQuestion] = useState(0);
    const newQuestionsArr = {
        qID: 0,
        questionTxt: newQuestions[currentQuestion],
    }
    const [questionCtx, setQuestionCtx] = useState(newQuestionsArr);
    const navigate = useNavigate()

    useEffect(() => {
        setQuestionCtx(prevState => ({
            ...prevState,
            qID: currentQuestion,
            questionTxt: newQuestions[currentQuestion], 
        }));
    }, [currentQuestion]);

    const updateNextQuestion = () => {
        if (!(currentQuestion >= newQuestions.length)) {
            setCurrentQuestion((nextCurrentQuestion) => nextCurrentQuestion   1);
        }
        else{
            navigate('/result')
        }
    };

    const updatePrevQuestion = () => {
        if (currentQuestion <= 0) {
            console.log(`No more questions`);
        } else {
            setCurrentQuestion((prevCurrentQuestion) => prevCurrentQuestion - 1);
        }
    };

    return (
        <QuestionContext.Provider
            value={{ questionCtx, updateNextQuestion, updatePrevQuestion }}>
            {props.children}
        </QuestionContext.Provider>
    );
};

export default SectionState;

Linter throws the following warning

React Hook useEffect has a missing dependency: 'newQuestions'. Either include it or remove the dependency array

If I add newQuestions in the dependency array, it results in re-rendering loop. I can't declare either newQuestions or questionCtx state inside useEffect as it is used elsewhere in the code.

I can see that I have to update the questionTxt. What should I do here to update the said value and remove the warning?

CodePudding user response:

A new newQuestions object is created at every render. usEffect is triggered when one of the dependencies changes. Hence the infinite render loop.

  1. If the newQuestions is a constant that depends on a json you import from a file, you could move it outside of the component as mentioned in @CertainPerformance answer. codesandbox

  2. If for some reasons you want to declare the newQuestions variable inside of your component, you could use useMemo hook. codesandbox

  3. You could disable the lint rule which is probably not a good idea.

I'm not really sure what you trying to achieve, but it seems like you probably don't need the useEffect and might have some redundant states.

Maybe you could use only one state, and get rid of the useEffect. You only need one state to keep track of the current question, and calculate other variables in each render.

  const [currentQuestion, setCurrentQuestion] = React.useState(0);

  const questionCtx = React.useMemo(
    () => ({
      qId: currentQuestion,
      questionTxt: newQuestions[currentQuestion]
    }),
    [currentQuestion]
  );

codesandbox

You could read more about managing state in the react beta documentation.

CodePudding user response:

The problem is that the newQuestions array is computed anew each time the function runs, and so won't be === to the old array (and so will run every render). If newQuestions depended on other React values, the usual fix would be to memoize it with useMemo, but because it looks to depend only on a static imported value, you may as well just declare it outside the component (which means it doesn't need to be a dependency anymore either).

It also looks like you don't care about the keys, only the values - so, easier to use Object.values than Object.keys.

import { React, useState, useEffect } from "react";
import QuestionContext from "./QuestionContext";
import questions from "../data/questions.json";
import { useNavigate } from "react-router-dom";

const newQuestions = Object.values(questions.content).map(
    val => val.question
);
  • Related