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.
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. codesandboxIf for some reasons you want to declare the
newQuestions
variable inside of your component, you could useuseMemo
hook. codesandboxYou 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]
);
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
);