Issue
I am struggling with value of states being set to 'undefined' after page refresh.
Inside my useEffect hook I generate a random number to pull a random question object from the array. On first render everything works fine, but when I refresh everything crashes because of what I expect is undefined values as a result of the useEffect only running once, therefore the values being loaded in through nextQuestion() doesn't rerun on refresh.
(Uncaught TypeError: Cannot read properties of undefined (reading 'X'))
This is the error I'm getting on every line of code that uses one of the question state values after page refresh.
Here is my page:
const Quiz_Play = () => {
const {questions} = useContext(QuizContext) as IQuizContext; // get array of questions from contexts
const [firstRender, setFirstRender] = useState(false);
useEffect(() => {
generateRandomNumber()
nextQuestion()
setFirstRender(true);
}, [firstRender]);
const [questionNum, setQuestionNum] = useState<number>(1)
const [questionText, setQuestionText] = useState<string>("Which company developed this game?")
const [questionImageId, setQuestionImageId] = useState<number>(21)
const [questionOptions, setQuestionOptions] = useState<string[]>(["Activision", "Ubisoft", "Treyarch"])
const [questionCorrect, setQuestionCorrect] = useState<string>("Treyarch")
const [questionsAnswered, setQuestionsAnswered] = useState<number>(0);
const [correctAnswers, setCorrectAnswers] = useState(0);
const [random, setRandom] = useState<number>(0)
const generateRandomNumber = () => {
const rand = Math.floor(Math.random() * questions.length);
console.log("rand num is " + rand)
setRandom(rand)
}
const nextQuestion = () => {
generateRandomNumber()
const q = questions[random]
console.log(q)
setQuestionNum(questionsAnswered+1)
setQuestionsAnswered(questionsAnswered)
setQuestionImageId(q.gameId)
setQuestionText(q.question)
setQuestionOptions(q.options)
setQuestionCorrect(q.correct)
}
return (
<div>
<Logo />
<div className="wrapper">
<h1 className="quizInfo_h1"> Question {questionNum} </h1>
<h2 className="quizQuestion_h2 text-align-center">{questionText}</h2>
<p className="correctCounter_p text-align-center no-margin "><span style={{color: '#f2c112'}}>{correctAnswers}</span>/{questionsAnswered}</p>
<div className="quizImage">
<Game_ById id={questionImageId}/>
</div>
</div>
<div className="quiz-options">
<Link to="/"><button className="btn small-med-btn quiz-option-btn" id="quiz-option-1">{questionOptions[0]}</button></Link>
<Link to="/"><button className="btn small-med-btn quiz-option-btn" id="quiz-option-2">{questionOptions[1]}</button></Link>
<Link to="/"><button className="btn small-med-btn quiz-option-btn" id="quiz-option-3">{questionOptions[2]}</button></Link>
</div>
<p style={{marginTop: 10, color:"rgba(255, 255, 255, 0.5)"}} className="text-align-center">
Don't want to play anymore? Feeling like a <Link to="/quiz" className="yellowlink">coward</Link>?
</p>
</div>
);
}
export default Quiz_Play;
Obviously the following code is only for visual representation, but I was thinking maybe something like this would solve my problem?
useEffect(() => {
generateRandomNumber()
nextQuestion()
}, [onWindowRefresh]);
So that the useEffect hook runs the code inside it once, but also if the page refreshes.
I have been scouring stack overflow and the internet but without prevail. I was hoping someone with abit more experience would be able to point me in the right direction as to where I go from here.
Let me know if you want me to post any additional code. Or if you need any additional details of some sort. Huge thanks in advance to anyone willing to spend their personal time to help me.
Solution
Add questions
in the useEffect dependency array to fire useEffect again when the question list changes
useEffect(() => {
if(questions.length > 0){
generateRandomNumber()
nextQuestion()
setFirstRender(true);
}
}, [firstRender, question]);
Answered By - Sachila Ranawaka
Answer Checked By - - Timothy Miller (ReactFix Admin)