2

I am a React beginner and I am trying to create a small game within the browser where an array of numbers is displayed, and the user must correctly choose the sum of all the numbers from a multiple choice selection. I have a button that starts the game via an onClick callback as shown below.

    const [sequence, setSequence] = useState([]);
    const [sum, setSum] = useState(0)
    const [countdown, setCountdown] = useState(10);
    const [multipleChoice, setMultipleChoice] = useState([]);

    const createSequence = () => {
        let newSequence = []
        for (let i = 0; i < 6; i++) {
            newSequence.push(Math.floor(Math.random() * 9) + 1);
        }
        setSequence(newSequence);
    }

    const createRandomAnswers = () => {
        let multipleChoiceArray = someRandomArrayFunction();
        setMultipleChoice(multipleChoiceArray);
    }

    const calculateSum = () => {
        // calculates sum of current number sequence and updates sum state
        let total = 0;
        for (var i in sequence) {
            total += sequence[i];
        }
        setSum(total);
    }

    const createGame = () => {
        createSequence();
        console.log("calculating sum")
        calculateSum();
        createRandomAnswers();
    }

    return (
        <>
            <p> {sum}</p>
            <h2>{sequence}</h2>
            <button onClick={createGame}>Start Game</button>
            <Choice value={multipleChoice[0]} />
            <Choice value={multipleChoice[1]} />
            <Choice value={multipleChoice[2]} />
        </>
    );

These three functions each update state variables using state hooks. However, each correct values of each are not in sync.

For example, if I had an array sequence of [1, 2, 3, 4], the sum should display as 10. When createGame is called, the sequence updates, but the sum does not update until the second time createGame is called, at which point the sequence updates to a new number, and the sum is set to 10.

createGame();
// Sequence displayed as [1, 2, 3, 4]
// sum displayed as 0 (initial state)
// multiple choice displayed as blank (initial state)

createGame();
// Sequence updates to random, e.g. [3, 1, 3, 2]
// sum displayed as 10 (sum from previous sequence)
// multiple choice displayed as blank (initial state)

Why do these values lag behind and update one at a time, when they are all being called within createGame? I know I am probably misunderstanding some core React principle, but any help would be appreciated.

dunnand
  • 21
  • 2
  • 1
    Welcome to SO! I don't see any React code here. Can you show a [mcve] please rather than pseudocode? "These three functions each update state variables using state hooks" -- please show, don't tell, because sometimes the devil is in the details, like how you're making these calls or whether these functions are doing no-nos like mutating state or reading stale state. These are very common bugs in React. – ggorlen Jul 10 '21 at 18:44
  • 1
    Refer to [this](https://stackoverflow.com/questions/36085726/why-is-setstate-in-reactjs-async-instead-of-sync) question. You'll probably find what you're looking for :) – Rukshan Jayasekara Jul 10 '21 at 18:48
  • @ggorlen I updated my code above, sorry not sure of the best way to clarify edits correctly, but that code should be more clear. Reading up on the other response, that may clarify my problem – dunnand Jul 10 '21 at 18:50
  • This is a great clarification, thanks. Yes, the answer is that when you call `setFoo` you won't see the new values until the next render. `calculateSum` is reading the old empty `sequence`. – ggorlen Jul 10 '21 at 18:52
  • You were both very helpful, thank you! – dunnand Jul 10 '21 at 22:27

1 Answers1

2

This is because useState hooks work asynchronously, meaning that the change does not occur immediately after the setState function is called. If you want that some action 'A' happens immediately after change in state 'total' then you can use useEffect and add a dependency for it such that when XYZ changes(total in your case), setSum(value). Make 'total' as state variable and do these changes:

const [total, setTotal] = useState(0)

const calculateSum = () => {
    // calculates sum of current number sequence and updates sum state
    let total = 0;
    for (var i in sequence) {
        total += sequence[i];
    }
    setTotal(total);
}


 useEffect(() => {
        setSum(total);
         }, [total]);
  • Your answer would be greatly improved if you showed and explained an example to answer the question here. Links are best used to point to futher information/resources after providing the answer. Better post = more reputation. – WorkingMatt Jul 26 '22 at 14:48
  • @WorkingMatt Thanks for the correction. I am a new contributor here so I will try not to do such mistakes in the future. I edited the answer. – Mahnoor Ali Jul 29 '22 at 20:41