如何在 ReactJS 中启动和停止计时器显示

How to start and stop timer display in ReactJS

我正在尝试在 ReactJS 中创建一个番茄钟。我无法让计时器停止倒计时。

PomView.js

const PomView = () => {
    const [timer, setTimer] = useState(1500)    // 25 minutes
    const [start, setStart] = useState(false)
    var firstStart = useRef(true)
    var tick;

    useEffect( () => {
        if (firstStart.current) {
            console.log("first render, don't run useEffect for timer")
            firstStart.current = !firstStart.current
            return
        }

        console.log("subsequent renders")
        console.log(start)
        if (start) {
            tick = setInterval(() => {
            setTimer(timer => {
                timer = timer - 1
                console.log(timer)
                return timer       
                }
            )
        }, 1000)
    } else {
        console.log("clear interval")
        clearInterval(tick);
    }
    }, [start])

    const toggleStart = () => {
        setStart(!start)
    }


    const dispSecondsAsMins = (seconds) => {
        // 25:00 
        console.log("seconds " + seconds)
        const mins = Math.floor(seconds / 60)
        const seconds_ = seconds % 60
        return mins.toString() + ":" + ((seconds_ == 0) ? "00" : seconds_.toString())
    }

    return (
        <div className="pomView">
            <ul>
                <button className="pomBut">Pomodoro</button>
                <button className="pomBut">Short Break</button>
                <button className="pomBut">Long Break</button>
            </ul>
            <h1>{dispSecondsAsMins(timer)}</h1>
            <div className="startDiv">
                {/* event handler onClick is function not function call */}
                <button className="startBut" onClick={toggleStart}>{!start ? "START" : "STOP"}</button>
                {start && <AiFillFastForward className="ff" onClick="" />}
            </div>
        </div>
    )
}

export default PomView

虽然 clearInterval 在 useEffect 的 else 部分运行,但计时器继续计时。不知道是不是因为useEffect中的异步setTimer方法。我想知道我写的代码有什么问题。

您将计时器引用存储在 tick 中,但每次组件重新渲染时,前一次渲染的 tick 值都会丢失。您还应该将 tick 存储为 React ref.

你也在改变 timer 状态。

setTimer((timer) => {
  timer = timer - 1; // mutation
  return timer;
});

就是return当前值减1:setTimer((timer) => timer - 1);

代码

const PomView = () => {
  const [timer, setTimer] = useState(1500); // 25 minutes
  const [start, setStart] = useState(false);
  const firstStart = useRef(true);
  const tick = useRef(); // <-- React ref

  useEffect(() => {
    if (firstStart.current) {
      firstStart.current = !firstStart.current;
      return;
    }

    if (start) {
      tick.current = setInterval(() => { // <-- set tick ref current value
        setTimer((timer) => timer - 1);
      }, 1000);
    } else {
      clearInterval(tick.current); // <-- access tick ref current value
    }

    return () => clearInterval(tick.current); // <-- clear on unmount!
  }, [start]);

  ...
};

useEffect( () => {
    const tick= setInterval(fun, 1000);
    return ()=>{
      clearInterval(tick);
    }
}, [])

useEffect有自己的释放方式