首页 > 解决方案 > SetInterval 在 React 功能组件中不起作用

问题描述

您能否向我解释一下我的 Timer 组件出了什么问题以及如何解决?
停止和重置按钮没有按我的计划工作。
停止按钮必须停止计时器(这意味着status=false),但保存最后一个runningTime值。
重置按钮必须清除两个值 ( status=false, runningTime=0) 但它只清除时间戳的列表,status并且runningTime保持不变。

我想我使用setInterval函数的方式有问题,但我无法弄清楚到底是什么。

import React, { useEffect, useState, useRef } from "react";

const initialState = {
  status: false,
  runningTime: 0,
  timestamps: []
};

export const Timer = () => {
  const [watchData, setWatchData] = useState(initialState);

  let timer = useRef();

  useEffect(() => {
    return () => {
      clearInterval(timer);
    };
  }, [timer]);

  const getUnits = (time) => {
    const seconds = time / 1000;

    const min = Math.floor(seconds / 60).toString();
    const sec = Math.floor(seconds % 60).toString();
    const msec = (seconds % 1).toFixed(3).substring(2);

    return `${min}:${sec}:${msec}`;
  };

  const handleClick = () => {
    const { status } = watchData;

    if (!status) {
      const startTime = Date.now() - watchData.runningTime;
      timer = setInterval(() => {
        setWatchData((prevState) => ({
          ...prevState,
          status: true,
          runningTime: Date.now() - startTime
        }));
      });
    } else {
      clearInterval(timer.current);
      setWatchData((prevState) => ({ ...prevState, status: false }));
    }
  };

  const handleReset = () => {
    clearInterval(timer.current);
    setWatchData({ ...initialState });
  };

  const handleLap = () => {
    const timestamp = getUnits(watchData.runningTime);
    console.log(timestamp);
    setWatchData((prevState) => ({
      ...prevState,
      timestamps: [...watchData.timestamps, timestamp]
    }));
  };

  return (
    <>
      <p>{getUnits(watchData.runningTime)}</p>
      <button onClick={handleClick}>
        {watchData.status ? "Stop" : "Start"}
      </button>
      <button onClick={handleReset}>Reset</button>
      <button onClick={handleLap}>Lap</button>
      {watchData.timestamps.length > 0 && (
        <ul>
          {watchData.timestamps.map((t, index) => (
            <li key={index}>{t}</li>
          ))}
        </ul>
      )}
    </>
  );
};

https://codesandbox.io/s/epic-hofstadter-byczm

标签: reactjsreact-hooks

解决方案


您不应该重新影响timer变量,但是您可以更新属性timer.current,这是对您的代码的更正:

const handleClick = () => {
  const { status } = watchData;
  clearInterval(timer.current);

  if (!status) {
    const startTime = Date.now() - watchData.runningTime;
    timer.current = setInterval(() => {
      setWatchData((prevState) => ({
        ...prevState,
        status: true,
        runningTime: Date.now() - startTime
      }));
    });
  } else {
    setWatchData((prevState) => ({ ...prevState, status: false }));
  }
}

您也可以像这样更新效果(以清除组件卸载时的间隔):

useEffect(() => {
  return () => clearInterval(timer.current);
}, []);

推荐阅读