0

The props on the 'GameState' component do not update correctly after changing their state from, they are always one iteration behind what the actual value of the state is as shown in the GIF and the sate is always one iteration behind when I try console.logging it too

enter image description here

import GameInfo from './components/GameInfo';
import GameState from './components/GameState';
import InputField from './components/InputField';
import StartButton from './components/StartButton';
import TheWord from './components/TheWord';
import WordsBox from './components/WordsBox';

function App() {
  const [currentDifficulty, setCurrentDifficulty] = useState({
    time: 7,
    wordCount: 15,
  });

  const changeDifficultyHandler = (difficulty) => {
    setCurrentDifficulty(difficulty);
  };

  return (
    <div>
      <header>
        Type Master
      </header>
      <main>
        <GameInfo onChangeDifficulty={changeDifficultyHandler} />
        <TheWord />
        <InputField />
        <StartButton />
        <WordsBox />
        <GameState difficulty={currentDifficulty} />
      </main>
    </div>
  );
}

export default App;
import React from 'react';

const GameState = (props) => {
  return (
    <div>
      <div>Time Left: {props.difficulty.time} Seconds</div>
      <div>Score: 0 From {props.difficulty.wordCount}</div>
    </div>
  );
};

export default GameState;

expected values:

  const levels = {
    Easy: {
      time: 7,
      wordCount: 15,
    },
    Normal: {
      time: 5,
      wordCount: 25,
    },
    Hard: {
      time: 3,
      wordCount: 30,
    },
  };

Code at the GameInfo component:

import { useState } from 'react';

const GameInfo = (props) => {
  const levels = {
    Easy: {
      time: 7,
      wordCount: 15,
    },
    Normal: {
      time: 5,
      wordCount: 25,
    },
    Hard: {
      time: 3,
      wordCount: 30,
    },
  };

  const [difficulty, setDifficulty] = useState(levels.Easy);

  const changeDifficultyHandler = (event) => {
    setDifficulty(levels[event.target.value]);
    props.onChangeDifficulty(difficulty);
  };
  return (
    <div>
      You Are Playing On The{' '}
      <span>
        [
        <select onChange={changeDifficultyHandler}>
          <option value='Easy'>Easy</option>
          <option value='Normal'>Normal</option>
          <option value='Hard'>Hard</option>
        </select>
        ]
      </span>{' '}
      Difficulity & You Have{' '}
      <span className='text-main font-poppins font-bold'>
        [ {difficulty.time} ]
      </span>{' '}
      Seconds To Type The Word.
    </div>
  );
};

export default GameInfo;
Amir Adel
  • 23
  • 3
  • `` Can you show your code at `onChangeDifficulty` and select button in ? – Peppers Nov 16 '22 at 02:50
  • Done, edited the queston to include the code for the GameInfo Component and here's a quick codesandbox minus the styles of the app as I am using tailwind to style this: [(https://rc5s1n.csb.app/](https://rc5s1n.csb.app/) – Amir Adel Nov 16 '22 at 03:50

2 Answers2

0

First import useEffect to your file

import {useEffect} from "react"

then,

Add this to your first code and try,

useEffect(() => {

}, [props.onChangeDifficulty(difficulty)]);

You have add this in your GameInfo.jsx.

Ankit
  • 1,359
  • 1
  • 2
  • 15
  • I already tried that, even when having useEffect the console.log inside it is always one iteration late, note that the value for currentDifficulty is coming from another component through a handler function passed as props which brings the value to the App component to be stored in the currentDifficulty state, then passed down to yet another component to show the change in the UI, maybe this scenario requires a different workaround than the useEffect hook? – Amir Adel Nov 16 '22 at 02:44
  • Can you make a codesandbox of this? – Ankit Nov 16 '22 at 03:12
  • here's a quick codesandbox minus the styles of the app as I am using tailwind to style this: [(https://rc5s1n.csb.app/](https://rc5s1n.csb.app/) – Amir Adel Nov 16 '22 at 03:51
  • That seemed to do the trick, thank you. – Amir Adel Nov 16 '22 at 05:22
  • Could you explain though what exactly does the empty useEffect exactly do? because I cannot fully make sense out of it – Amir Adel Nov 16 '22 at 05:29
  • Calling a function from the dependency array is not correct. – sallf Nov 16 '22 at 05:30
0

Set state is async, so in changeDifficultyHandler, difficulty is still referencing the old value because it hasn't updated yet. A simple fix would be changing props.onChangeDifficulty(difficulty) to props.onChangeDifficulty(levels[event.target.value]).

That said, storing the same state in two places isn't best practice. Why not just keep the state in App and pass it to GameInfo as a prop like <GameInfo onChangeDifficulty={changeDifficultyHandler} difficulty={currentDifficulty} />?

sallf
  • 2,583
  • 3
  • 19
  • 24