In my code (React Native) to avoid the "Too many re-renders" error I used a useEffect hook. I would expect the useEffect to run when the component PracticeTest first runs but it doesn’t. The problem with hte code isn’t that the useEffect runs during every render, but rather it doesn’t run at all. Can someone explain how I can fix it?
function PracticeTest({setCurrentMode}){
const [index, setIndex] = useState(0);
const [selected, setSelected] = useState(0);
const [feedback, setFeedback] = useState('');
const [score, setScore] = useState(0);
// Create 'isCorrect' var to designate if ready to move on to the next question
const [isCorrect, setIsCorrect] = useState(false);
// get only the questions needed
testQuestions = require('./testQuestions.json');
let questionAnswers = [];
let firstQuestionIndex;
let lastQuestionIndex;
let testLength = lastQuestionIndex - firstQuestionIndex;
const [shuffled, setShuffled] = useState(false);
const [done, setDone] = useState(false);
useEffect(() => {
questionAnswers = [];
for(var i=0; i<testQuestions.length; i++){
if((i>=firstQuestionIndex) && (i< lastQuestionIndex)){
questionAnswers.push(testQuestions[i]);
}
}
shuffle(questionAnswers);
testLength = 10;
shuffled = true;
});
// if quizType is "practiceTest" then the questions will be randomly selected and jumbled
// Source for shuffle function (modified by me): https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
if(shuffled){
console.log("ALREADY SHUFFLEDDDDDDDDDDDDDD");
}
else{
let currentIndex = array.length;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
let randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
console.log("Shuffle!!");
}
}
let question = questionAnswers[index].question;
function check(){
if(selected == questionAnswers[index].correctID){
// add 1 point to score if the feedback is blank
if(feedback==''){
setScore(score+1);
}
setFeedback('Correct!');
setIsCorrect(true);
}
else{
setFeedback("Try again");
}
}
function proceed() {
if((index + 1) >= testLength){
// Code to be executed if done
setDone(true);
}
else{
setIndex(index + 1);
setIsCorrect(false);
setColor1('#FF6868');
setColor2('#FF6868');
setColor3('#FF6868');
setColor4('#FF6868');
setFeedback('');
}
}
const [color1, setColor1] = useState('#FF6868');
const [color2, setColor2] = useState('#FF6868');
const [color3, setColor3] = useState('#FF6868');
const [color4, setColor4] = useState('#FF6868');
return (
<View>
{done ?
<QuizDone setCurrentMode={setCurrentMode} score={score} testLength={testLength}/>
:
<View style={styles.app}>
<Text style={styles.logo}>Testing your Knowledge</Text>
<Text style={styles.question}>{question}</Text>
<AnswerChoices setSelected={setSelected} index={index} questionAnswers={questionAnswers} color1={color1} color2={color2} color3={color3} color4={color4} setColor1={setColor1} setColor2={setColor2} setColor3={setColor3} setColor4={setColor4} />
<BackButton setCurrentMode={setCurrentMode}></BackButton>
{/** Feedback */}
<View style={{
position: 'absolute',
width: 144,
height: 42,
top: 595,
left: 63,
justifyContent: 'center',
}}>
<Text>{feedback}</Text>
</View>
{/** Check Answer */}
{
isCorrect ?
<TouchableOpacity style={styles.checkButton} onPress={proceed}>
<Text style={{color: 'white', alignSelf: 'center',}}>Move on</Text>
</TouchableOpacity>
:
<TouchableOpacity style={styles.checkButton} onPress={check}>
<Text style={{color: 'white', alignSelf: 'center',}}>Check</Text>
</TouchableOpacity>
}
</View>
}
</View>
)
}
I tried may things, like adding a console.log to see if the useEffect was even running, which it didn’t. And because the code inside of the useEffect isn’t running, the array "questionAnswers" has no value.
2
Answers
If You don’t use a dependency array, the useEffect will run on every render and is useless. If you only need the effect to run on initial render, you can use an empty array as a dependency array:
useEffect(() => {...}, [])
I think there is an issue with how you are setting the variables and also the dependency array is missing, set dependency array to
[]
if you want it to run only once -> on mountSet variable like this: