在 React 中设置状态在超时后应该如何实现?

huangapple go评论131阅读模式
英文:

How to set state in React after a timeout?

问题

在React中,您可以使用setSpinning来更新spinning状态,但是在您的代码中,setSpinning(false)被放置在一个setTimeout函数内部,这会导致它立即被调用,而不会等待setTimeout完成。要解决这个问题,您可以将setSpinning(false)移到setTimeout函数外部,以确保它在动画完成后再设置spinningfalse。以下是更改后的代码片段:

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));
  }
  // 移到这里确保在动画完成后再设置为false
  setTimeout(() => {
    setSpinning(false);
  }, i * (i * 1.25));
};

通过将setSpinning(false)移动到setTimeout函数的外部,您可以确保在动画完成后才将spinning设置为false

英文:

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 &quot;react&quot;;

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 = () =&gt; {
  const [number, setNumber] = useState(0);
  const [spinning, setSpinning] = useState(false);

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

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

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

export default RouletteWheel;

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

答案1

得分: 1

I just added useEffect to your code, added a 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;
英文:

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 &quot;react&quot;;
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 = () =&gt; {
const [number, setNumber] = useState(0);
const [spinning, setSpinning] = useState(false);
const getRandomNumber = () =&gt; {
return numbers[Math.floor(Math.random() * numbers.length)];
};
useEffect(() =&gt; {
if (!spinning) {
// additional code to execute once the spinning state has been updated
}
}, [spinning]);
const spin = () =&gt; {
const num = getRandomNumber();
let numColor = &quot;&quot;;
const i = numbers.length * 2 + numbers.indexOf(num) + 1;
for (let j = 0; j &lt; i; j++) {
setTimeout(() =&gt; {
setSpinning(true);
if (green.includes(numbers[j % numbers.length])) {
numColor = &quot;green&quot;;
} else if (red.includes(numbers[j % numbers.length])) {
numColor = &quot;red&quot;;
} else if (black.includes(numbers[j % numbers.length])) {
numColor = &quot;black&quot;;
}
document.getElementById(&quot;numberDisp&quot;).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 (
&lt;div className=&quot;roulette&quot;&gt;
&lt;h1 id=&quot;numberDisp&quot;&gt;{number}&lt;/h1&gt;
&lt;button onClick={spin} disabled={spinning}&gt;
{spinning ? &quot;Spinning...&quot; : &quot;Spin the wheel!&quot;}
&lt;/button&gt;
&lt;/div&gt;
);
};
export default RouletteWheel;

huangapple
  • 本文由 发表于 2023年5月13日 23:14:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76243473.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定