【问题标题】:setInterval in react does not update the setStatereact中的setInterval不更新setState
【发布时间】:2021-07-19 15:40:27
【问题描述】:

我想做一个倒数计时器(25 分钟)。这是 App.js 文件中的代码

import './App.css';
import React, { useState } from 'react';

function App() {
  const [remainingTime, setRemainingTime] = useState(1500);
  const [seconds, setSeconds] = useState(0);
  const [minute, setMinute] = useState(remainingTime/60);

  function timer() {
    setRemainingTime(remainingTime-1);
    let newMinute = remainingTime/60;
    let minuteArray = [...newMinute.toString()];
    setMinute(parseInt(minuteArray.slice(0,2).join("")));
    setSeconds(remainingTime%60);
    console.log(minute);
    console.log(seconds);
  }

  return (
    <div className="App">
      <div className="pomodoro">
        <div className="timer">{minute}:{seconds}</div>
        <div className="button-container">
          <button className="start" onClick={() => setInterval(timer, 1000)}>start</button>
        </div>
      </div>
    </div>
  );
}

export default App;

间隔不更新状态值。分钟值始终为 25,秒值始终为 0。当我不使用 setInterval 而只使用这样的计时器功能时

<button className="start" onClick={timer}>start</button>

每次单击开始按钮时,值都会发生变化。任何想法?我知道我也应该使用 clearInterval 但我不知道该放在哪里。我应该创建一个包含 setInterval 和 clearInterval 的新函数吗?

【问题讨论】:

    标签: reactjs react-hooks setinterval use-state


    【解决方案1】:

    问题

    主要问题是 timer 回调中的陈旧状态附件。

    看来你让它比它需要的复杂了一点。

    解决方案

    1. minuteseconds 被认为是派生状态(来自 remainingTime),因此它不应该也存储在状态中,它很容易从状态中计算出来。
    2. 使用功能状态更新来更新之前状态的 remainingTime 状态,而不是回调入队的渲染周期状态。
    3. 使用 React ref 来保存间隔计时器 ref,因此可以清除间隔。
    4. 使用useEffect 挂钩返回一个清理函数,以便在卸载组件时清除所有运行间隔。

    代码:

    function App() {
      const timerRef = React.useRef();
    
      React.useEffect(() => {
        return () => clearInterval(timerRef.current);
      }, []);
    
      const [remainingTime, setRemainingTime] = React.useState(1500);
    
      function timer() {
        setRemainingTime((remainingTime) => remainingTime - 1);
      }
    
      const startTimer = () => {
        clearInterval(timerRef.current);             // clear any running interval
        setRemainingTime(1500);                      // reset state back to 25 minutes
        timerRef.current = setInterval(timer, 1000); // start/restart interval
      };
    
      const minute = String(Math.floor(remainingTime / 60)).padStart(2, 0);
      const seconds = String(remainingTime % 60).padStart(2, 0);
    
      return (
        <div className="App">
          <div className="pomodoro">
            <div className="timer">
              {minute}:{seconds}
            </div>
            <div className="button-container">
              <button className="start" onClick={startTimer}>
                start
              </button>
            </div>
          </div>
        </div>
      );
    }
    

    演示

    由于这可能是一个后续问题,请使用另一个 useEffect 来“监听”remainingTime 状态,当时间达到 0 时,清除间隔,将 remainingTime 重置回 1500(用于显示),并显示任何警报或警报或番茄休息/时间表的任何内容。

    【讨论】:

      【解决方案2】:

      每次重新渲染时,计时器设置为 1500。在此处使用闭包,点击时激活。

      function timer() {
          let t = remainingTime;
          setInterval(() => {
            t -= 1;
            setRemainingTime(t);
            let newMinute = t / 60;
            let minuteArray = [...newMinute.toString()];
            setMinute(parseInt(minuteArray.slice(0, 2).join("")));
            setSeconds(t % 60);
            console.log(minute);
            console.log(seconds);
          }, 1000); } 
      

      【讨论】:

        猜你喜欢
        • 2022-12-23
        • 2019-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 2016-09-22
        • 2018-02-06
        • 2023-04-09
        相关资源
        最近更新 更多