» React创建番茄工作法Pomodoro Web应用 » 2. 开发 » 2.3 定时器

定时器

给 “Start” 按钮“注能”使定时器动起来。修改 App.js 如下:

@@ -1,6 +1,54 @@
+import { useEffect, useRef, useState } from "react";
 import "./App.css";
+import { formatTime } from "./util";
+
+const POMODORO_SECONDS = 25 * 60;
 
 function App() {
+  const [seconds, setSeconds] = useState(POMODORO_SECONDS);
+  const [ticking, setTicking] = useState(false);
+
+  useEffect(() => {
+    if (seconds === 0) {
+      stopTimer();
+      alarm();
+    }
+  }, [seconds]);
+
+  // use the `useRef` hook to create a mutable object that persists across renders
+  const intervalIdRef = useRef(null);
+
+  const startTimer = () => {
+    setTicking(true);
+    intervalIdRef.current = setInterval(() => {
+      setSeconds((prevSeconds) => prevSeconds - 1);
+    }, 1000);
+  };
+
+  const stopTimer = () => {
+    setTicking(false);
+    if (intervalIdRef.current) {
+      clearInterval(intervalIdRef.current);
+    }
+  };
+
+  const resetTimer = () => {
+    stopTimer();
+    setSeconds(POMODORO_SECONDS);
+  };
+
+  const toggleTimer = () => {
+    if (ticking) {
+      stopTimer();
+    } else {
+      startTimer();
+    }
+  };
+
+  const alarm = () => {
+    // TODO: play some sound
+    console.log("Time's up!");
+  };
   return (
     <div className="app">
       <h1 className="app-name">
@@ -13,10 +61,15 @@ function App() {
         <span className="segment right-seg">Break</span>
       </div>
       <div className="card">
-        <h1 className="timer">25:00</h1>
-        <button className="control-btn">Start</button>
+        <h1 className="timer">{formatTime(seconds)}</h1>
+        <button className="control-btn" onClick={toggleTimer}>
+          {ticking ? "Pause" : "Start"}
+        </button>
       </div>
       <div className="settings">
+        <span className="setting-btn" onClick={resetTimer}>
+          Reset
+        </span>
         <span className="setting-btn">Settings</span>
       </div>
     </div>

定时器的“跳动”由 setInterval() 全局函数触发。每次调用时,它会返回一个唯一的 interval ID,可用于在事后通过 clearInterval() 去除定时器。

interval ID 在 React 的 useRef hook 函数帮助下在 startTimerstopTimer 两函数中实现配合控制使用。

微调一下 App.css

 .settings {
   display: flex;
-  justify-content: flex-end;
+  justify-content: space-between;
 }

添加新文件 util.js

export function formatTime(seconds) {
  const minutes = Math.floor(seconds / 60);
  const remainingSeconds = seconds % 60;

  const formattedMinutes = String(minutes).padStart(2, "0");
  const formattedSeconds = String(remainingSeconds).padStart(2, "0");

  return `${formattedMinutes}:${formattedSeconds}`;
}

点击 “Start” 按钮后可以看到定时器开始计时。

Pomodoro Ticking