How to set state in React after a timeout?

I am creating a simple roulette wheel in React and when I attempt to set the state spinning to false at the end of the function being run it does not change.

import React, { useState } from "react";

const numbers = [
  0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24,
  16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26,
];

const green = [0];

const red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36];

const black = [
  2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35,
];

const RouletteWheel = () => {
  const [number, setNumber] = useState(0);
  const [spinning, setSpinning] = useState(false);

  const getRandomNumber = () => {
    return numbers[Math.floor(Math.random() * numbers.length)];
  };

  const spin = () => {
    const num = getRandomNumber();
    let numColor = "";
    const i = numbers.length * 2 + numbers.indexOf(num) + 1;
    for (let j = 0; j < i; j++) {
      setTimeout(() => {
        setSpinning(true);
        if (green.includes(numbers[j % numbers.length])) {
          numColor = "green";
        } else if (red.includes(numbers[j % numbers.length])) {
          numColor = "red";
        } else if (black.includes(numbers[j % numbers.length])) {
          numColor = "black";
        }
        document.getElementById("numberDisp").style.color = numColor;
        setNumber(numbers[j % numbers.length]);
      }, j * (j * 1.25));
    }
    setSpinning(false);
  };

  return (
    <div className="roulette">
      <h1 id="numberDisp">{number}</h1>
      <button onClick={spin} disabled={spinning}>
        {spinning ? "Spinning..." : "Spin the wheel!"}
      </button>
    </div>
  );
};

export default RouletteWheel;

I’ve tried moving around the setSpinning but nothing has fixed it

>Solution :

i just added useEffect to your code, added an useEffect hook that listens for changes to the spinning state and executes any additional code you provide once the state has been updated. i also moved the setSpinning(false) call inside the loop to ensure that it’s executed only once the loop has completed. Calling setSpinning(false) immediately after the loop will not guarantee that the state has been updated by the time you call it. You can use the useEffect hook to wait for the state to update before executing additional code.

import React, { useState, useEffect } from "react";
    
    const numbers = [
      0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24,
      16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26,
    ];
    
    const green = [0];
    
    const red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36];
    
    const black = [
      2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35,
    ];
    
    const RouletteWheel = () => {
      const [number, setNumber] = useState(0);
      const [spinning, setSpinning] = useState(false);
    
      const getRandomNumber = () => {
        return numbers[Math.floor(Math.random() * numbers.length)];
      };
    
      useEffect(() => {
        if (!spinning) {
          // additional code to execute once the spinning state has been updated
        }
      }, [spinning]);
    
      const spin = () => {
        const num = getRandomNumber();
        let numColor = "";
        const i = numbers.length * 2 + numbers.indexOf(num) + 1;
        for (let j = 0; j < i; j++) {
          setTimeout(() => {
            setSpinning(true);
            if (green.includes(numbers[j % numbers.length])) {
              numColor = "green";
            } else if (red.includes(numbers[j % numbers.length])) {
              numColor = "red";
            } else if (black.includes(numbers[j % numbers.length])) {
              numColor = "black";
            }
            document.getElementById("numberDisp").style.color = numColor;
            setNumber(numbers[j % numbers.length]);
            if (j === i - 1) {
              // last iteration, so set spinning to false
              setSpinning(false);
            }
          }, j * (j * 1.25));
        }
      };
    
      return (
        <div className="roulette">
          <h1 id="numberDisp">{number}</h1>
          <button onClick={spin} disabled={spinning}>
            {spinning ? "Spinning..." : "Spin the wheel!"}
          </button>
        </div>
      );
    };
    
    export default RouletteWheel;

Leave a Reply