代码之家  ›  专栏  ›  技术社区  ›  Edward Tanguay

如何在React中获取setInterval来更改状态变量?

  •  0
  • Edward Tanguay  · 技术社区  · 3 年前

    我希望以下代码在用户登录后开始从一个数字开始倒计时。

    以下代码显示了简单的显示 0 在console.log中,但似乎没有设置状态变量 secondsLeft 到8,它也不倒计数这个变量。

    const [secondsLeft, setSecondsLeft] = useState(0);
    

    ...

    const handleButton = async () => {
        const response = await fetch('http://localhost:5001/login', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ username, password })
        });
        setUsername('');
        setPassword('');
        if (response.ok) {
            const data = await response.json();
            setCurrentUser(prev => ({ ...prev, ...data.user }));
            setMessage(`User: ${currentUser.firstName}`);
            setSecondsLeft(8);
            setInterval(() => {
                setSecondsLeft(prev => secondsLeft -1);
                console.log(secondsLeft);
            }, 1000);
        } else {
            setMessage('bad login');
        }
    }
    

    如何获取setInterval以减小的值 秒左侧 每秒钟?

    0 回复  |  直到 3 年前
        1
  •  1
  •   MarcRo Christian Fritz    3 年前

    这是React中一个非常常见的陷阱。可以理解的是,你似乎认为 setSecondsLeft 立即生效,但不会。因此, secondsLeft 不会是8开始。还有一个问题 秒左侧 没有在您的间隔中更新。这应该起作用:

        setSecondsLeft(8);
        setInterval(() => {
            setSecondsLeft(prev => {
              console.log(prev);
              return prev - 1;
            });
        }, 1000);
    
        2
  •  0
  •   Himanshu Singh    3 年前

    我试着用传统的方式做事,

    useEffect(() => {
      setInterval(() => {
         console.log("update", st);
         setSt((prev) => prev + 1);
      }, 500);
    }, []);
    

    但这不起作用,因为在运行期间 useState 在第一次渲染时,setInterval保持 copy of prev 对于它试图一次又一次更新的自身,例如: setSt(prev => prev+1) 但是由于prev总是=1,这是由于它本身具有闭包,所以该值并没有真正更新。


    然后我试着使用 useCallback 重新发出的钩子 function 依赖性变化,从而提供 prev 作为依赖,但遗憾的是,这也不起作用,很可能是由于与上述相同的原因,但方式不同。

    你可以在的注释掉的代码中看到我的尝试方式 codesandbox .


    最后,我尝试通过创建 setIntervalHack 它的工作原理类似于setInterval,但实际上不是setInterval。

    以下是我遵循的步骤。

    • 你可以想到 setInterval as a recursive 函数 间隔一段时间后反复运行 ,但缺点是我们无法将参数传递给它,因为更新会变得过时。
    • 所以,我向 setIntervalHack 其描述了要更新的新状态。
    • 为了等待一段时间,我使用 承诺 await 再次运行递归,并在调用递归函数的参数中 setIntervalHack ,我通过了 (当前状态+1)

    这就是我试图实现的类似功能的方式 setInterval

    这是代码的链接 https://codesandbox.io/s/happy-swartz-ikqdn?file=/src/focus.js

    注意:您可以在中继续/聚焦路线 codesandbox's browser 然后打开控制台

    PS:我对计数器做了增量,而不是减量。您可以通过每次递归递减来执行类似的操作

        3
  •  0
  •   Saeed Shamloo    3 年前

    事实上它是有效的,但你可以在控制台中看到 8. 每次运行setIntervall回调时,因为它只访问secondsLeft的初始值,并且在以后的更新中不识别secondsLeft的更新。您可以运行下面的示例,并在每次运行时间setInterval回调时看到它更新状态,但在控制台中您总是看到0。

    function App() {
      const [secondsLeft, setSeconds] = React.useState(0);
      let timerId = null;
      const handleClick = () => {
        setSeconds(8);
        timerId = setInterval(()=> {
          console.log(secondsLeft)
          setSeconds(prev => prev - 1);
        }, 1000)
      };
    
      React.useEffect(()=> {
        return ()=> clearInterval(timerId)
      }, [])
    
      return (
        <div>
          <h2>{secondsLeft}</h2>
          <button onClick={handleClick}>Start interval</button>
        </div>
      );
    }
    
    ReactDOM.render(React.createElement(App, {}), document.getElementById("root"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
    
    <div id="root"></div>