定时器
给 “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 函数帮助下在 startTimer
和 stopTimer
两函数中实现配合控制使用。
微调一下 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” 按钮后可以看到定时器开始计时。