首页 > 解决方案 > setInterval 和 clearInterval 不能一起工作

问题描述

所以我有一个 setInterval 和 clearInterval 函数来分别启动和停止一个计数器。这里的问题是每次我单击按钮时,状态都会设置为 true,并且会触发计时器功能来启动计时器。现在再次单击计时器,计时器不会停止,因为 refreshIntervalId 变量现在具有未定义的值。请帮我解决这个问题!

这是我的代码:

  if (timer == true) {
    console.log("timer is playing");
    refreshIntervalID = setInterval(timerFunction, 1000);
    console.log(refreshIntervalID);
  }

  if (timer == false) {
    console.log(refreshIntervalID);
    clearInterval(refreshIntervalID);
  }

这是完整的代码:

import React from "react";
import "./App.css";
import { useSelector, useDispatch } from "react-redux";
import { playToggle, reset } from "./actions/indexAction";

function Timer() {
  const timer = useSelector((state) => state.timer);
  const sessionLength = useSelector((state) => state.sessionLength);

  let minutes = sessionLength;
  let seconds = 60;
  var refreshIntervalID;

  const dispatch = useDispatch();

  let resetClick = () => {
    return dispatch(reset());
  };

  let timerFunction = () => {
    if (timer == true) {
      seconds--;
      console.log(seconds);
    }
  };

  if (timer == "reset") {
    minutes = sessionLength;
    seconds = 60;
    console.log("Is Reset");
  }

  if (timer == true) {
    console.log("timer is playing");
    refreshIntervalID = setInterval(timerFunction, 1000);
    console.log(refreshIntervalID);
  }

  if (timer == false) {
    console.log(refreshIntervalID);
    clearInterval(refreshIntervalID);
  }

  return (
    <div>
      <h1>Minutes: {minutes}</h1>
      <h1>Minutes: {seconds}</h1>
      <div>
        <button onClick={() => dispatch(playToggle())}>PLAY/PAUSE</button>
      </div>
      <div>
        <button onClick={resetClick}>RESET</button>
      </div>
    </div>
  );
}

export default Timer;

标签: javascriptreactjsredux

解决方案


这并不完全相关,因为与之相关的评论结果并不完全准确:

你绝对不想在 redux reducer 中产生副作用。让副作用发生在组件中。原因是 reducer 应该是纯的,以使整个通量体系结构正常工作。在 reducer 中唯一应该有副作用的情况是,如果您使用的是像redux-loop这样的中间件。否则你会很快得到一些非常难以维护的代码。

如果这是在功能组件中,我是这样做的:

function(){
  useEffect(()=>{
    if (timer) {
      console.log("timer is playing");
      const refreshIntervalID = setInterval(timerFunction, 1000);
      console.log(refreshIntervalID);
      return ()=>{
        clearInterval(refreshIntervalID)
      }
    }
  },[timer])
}

您必须清除 useEffect 挂钩内的间隔

编辑:添加有希望的工作代码

import React, { useEffect, useState } from 'react';
import './App.css';
import { useSelector, useDispatch } from 'react-redux';
import { playToggle } from './actions/indexAction';

function Timer() {
  const timer = useSelector((state) => state.timer);
  const sessionLength = useSelector((state) => state.sessionLength * 60); // session in seconds
  const [seconds, setSeconds] = useState(sessionLength);
  const [resetTimer, setResetTimer] = useState(0);

  const dispatch = useDispatch();

  let resetClick = () => {
    // Get a new value every time
    setSeconds(sessionLength);
    setResetTimer((value) => value + 1);
  };
  useEffect(()=>{
    // Set the timer to use the latest value when the session length changes.
    setSeconds(sessionLength)
  },[sessionLength])

  useEffect(() => {
    // Perform a side effect
    if (timer) {
      // Only run the timer when timer is truthy
      let timerFunction = () => {
        setSeconds((seconds) => seconds - 1);
      };
      console.log('timer is playing');
      const refreshIntervalID = setInterval(timerFunction, 1000);
      return () => {
        // Clean up timer before effect runs again (or on unmount)
        clearInterval(refreshIntervalID);
      };
    }
    // Restart timer whenever timer, sessionLength, or resetTimer changes
  }, [timer, resetTimer]);

  useEffect(() => {
    // If the timer has elapsed, stop the timer and reset.
    if (seconds <= 0) {
      dispatch(playToggle());
      setSeconds(sessionLength);
    }
  }, [seconds, dispatch, sessionLength]);

  return (
    <div>
      <h1>Minutes: {Math.floor(seconds / 60)}</h1>
      <h1>Minutes: {seconds % 60}</h1>
      <div>
        <button onClick={() => dispatch(playToggle())}>PLAY/PAUSE</button>
      </div>
      <div>
        <button onClick={resetClick}>RESET</button>
      </div>
    </div>
  );
}

export default Timer;

推荐阅读