Home > Software engineering >  How to properly handle useStates and useEffects in a Reactjs simple quiz app that is rendering every
How to properly handle useStates and useEffects in a Reactjs simple quiz app that is rendering every

Time:09-05

I'm building this simple quiz app in React where a random Korean hangul from a 40-item Data.json is displayed on the screen and users have 5 buttons with different answers which also come from Data.json.

For clarification, this is my application and the components I'm using. All components are siblings and live inside App.js.

Application screenshot

What the application is suppose to do is to create a 5-item random array and select one of these as the right answer before render. If the user taps the right button all the components should be re-rendered with different items. That's why I decided to use the useEffect() hook.

But what is currently happening is:

  • Every console.log is being displayed twice in the console. The first time variables are empty and in the second time they display the values they should have;
  • The randomOptions array has 10 items instead of 5;
  • If the <Question/> component is in the code then I get errors and console.log tells me the values from my useState hooks are undefined. If <Question/> is removed I get the console shown in the picture below.

Console for clarification:

Application Console

App.js component:

import './App.css';
import Data from './data/data.json';
import Header from './components/Header';
import Question from './components/Question';
import { useState, useEffect } from 'react';

function App() {
  const [index, setIndex] = useState(Math.floor(Math.random() * 5)); // Tells us what the index of the right answer is
  const [randomOptions, setRandomOptions] = useState([]); // Random array with 5 items from Data.json that are the options
  const [question, setQuestion] = useState([]); // The question that goes to <Question/> and people need to guess

  const randomizeButtonAnswer = () => {
    let previousArray = randomOptions;
    setRandomOptions([]);
      for (let i=0; i < 5; i  ){
        let randomNumber = Math.floor(Math.random() * Data.length); 
          if (!randomOptions.includes(randomNumber) && !previousArray.includes(randomNumber)) {
            setRandomOptions(randomOptions.push(randomNumber)); 
          } else {
            i--;
          }
        }
      setRandomOptions(randomOptions.sort(() => Math.random() - 0.5));
      setQuestion(randomOptions[index]);
    }
  
  useEffect(() => {
    randomizeButtonAnswer();
  }, [index])

  console.log('index is '   index); // 3
  console.log('randomOptions is '   randomOptions); // 33,9,3,26,8,20,0,12,29,25
  console.log('question is '   question); //26

  return (
    <div className="App">
        <Header />
        <Question answer={question}/>
    </div>
  );
}

export default App;

Question.js component

import './Question.css';
import Data from '../data/data.json';

const Question = ({ answer }) => {

    return (
        <div className="question-wrapper">
            <h1 className="question"><span className="highlight">{Data[answer].character}</span></h1>
                <span>{Data[answer].character} = {Data[answer].char_id} </span>
        </div>
    );
}
 
export default Question;

I'm completely lost here. Not sure why things are being rendered twice or why when present <Question/> makes my variables become undefined. I assume something inside randomizeButtonAnswer is broken but I've been unable to find what exactly.

Small disclaimer: I'm a complete noob to React so if I'm doing something that is wrong or a bad practice do let me know.

Thanks for your time!

CodePudding user response:

I tried to fix your code but it's complicated and wrong at many points so I decided to write the function myself. Sorry about that.

Here is the codesandbox

there are many problem with your code.

  1. About log 2 times. It is <StrictMode />
  2. You should understand more about React State. It not change right in the place you write it, it's queue up and update when your functions end. set 1 state 2 time in 1 function does not mean anything.
  3. Don't do i-- in for loop like that which is complicated to understand how things goes and hard to debug. use while loop or for best just don't. check the condition and log out or have some handle for it.
  4. Wanna log? log inside React Component? use useEffect.
  5. Default state of Question is not [].
  6. Always do conditional render if default value of state is possible be either null or undefined.

Good luck with your React journey.

  • Related