reactjs react中的setInterval不会更新setState

brccelvz  于 2022-12-26  发布在  React
关注(0)|答案(2)|浏览(378)

我想做一个倒计时器(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的新函数吗?

vzgqcmou

vzgqcmou1#

问题

    • 主要问题是timer回调中的状态附件过时。**

在间隔回调中,设置新值的标准形式不起作用,因为回调在每次运行时都使用相同的(未更新的)状态值:

***-这不起作用:***setValue(value + 1)

溶液

使用另一种形式设置新值:

***-这将起作用:***setValue((value) => value + 1)

此形式允许回调在每次运行时获取具有最新状态的新值。

详情

  1. minuteseconds被认为是派生状态(从remainingTime),因此不应将其存储在状态中,它很容易从状态计算出来。
    1.使用功能状态更新从先前状态更新remainingTime状态,而不是从回调入队的渲染周期状态更新remainingTime状态。
    1.使用React参考值保持间隔定时器参考值,以便清除间隔。
    1.使用useEffect钩子返回一个clean up函数,以清除组件卸载时的任何运行间隔。
    代码:
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(* 用于显示 *),并显示任何报警或警报或番茄休息/时间表的任何内容。

s5a0g9ez

s5a0g9ez2#

每次重新渲染时,计时器设置为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); }

相关问题