前言
大佐在群里出了一道题是在页面上显示一个毫秒级的定时器,下面是大佐发的答案,赶紧收藏起来
代码
index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
App.js
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
import "./styles.css";
export default function App() {
const phase = useRef("idle");
const base = useRef();
const [btnText, setBtnText] = useState("Start");
const [accTime, setAccTime] = useState(0);
const [elsTime, setElsTime] = useState(0);
const time = useMemo(() => accTime + elsTime, [accTime, elsTime]);
useEffect(() => {
let stopped = false;
(function tick() {
if ("running" === phase.current) {
setElsTime(performance.now() - base.current);
}
!stopped && requestAnimationFrame(tick);
})();
return () => (stopped = true);
}, []);
const onClickButton = useCallback(() => {
phase.current = {
idle: "running",
paused: "running",
running: "paused"
}[phase.current];
setBtnText(
{
idle: "Start",
paused: "Resume",
running: "Pause"
}[phase.current]
);
if ("running" === phase.current) {
base.current = performance.now();
} else if ("paused" === phase.current) {
setAccTime(time);
setElsTime(0);
}
}, [time]);
return (
<div className="App">
<h1>{(time * 1e-3).toFixed(3)}</h1>
<button onClick={onClickButton}>{btnText}</button>
</div>
);
}
styles.css
* {
box-sizing: border-box;
}
body {
background-color: #444;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
h1 {
background-color: #222;
color: white;
padding: 12px 20px;
min-width: 200px;
text-align: center;
}
button {
padding: 8px 32px;
border: none;
background-color: slateblue;
color: white;
cursor: pointer;
min-width: 200px;
font-size: 16px;
font-family: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande",
"Lucida Sans", Arial, sans-serif;
}
知识点
requestAnimationFrame
requestAnimationFrame() 方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。
performance.now()
performance.now() 方法返回一个 DOMHighResTimeStamp,以毫秒为单位,精确度可达 5 微秒(在实现中,将时间戳的第 12 位四舍五入到最接近的整数)。
useCallback
useCallback 会返回一个 memoized 回调函数。
在这里使用 useCallback 的原因是因为 onClickButton 函数会在每次渲染时都会被创建,如果不使用 useCallback 的话,每次渲染都会创建一个新的函数,这样会导致每次渲染都会触发 useEffect,这样就会导致页面卡顿。
useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
在这里使用 useRef 的原因是因为 useRef 返回的 ref 对象在组件的整个生命周期内保持不变,这样就可以保证 phase.current 的值在每次渲染时都不会被重置。
useMemo
useMemo 会在渲染期间执行,它会返回一个 memoized 值。
在这里使用 useMemo 的原因是因为 useMemo 会在渲染期间执行,它会返回一个 memoized 值,这样就可以保证 time 的值在每次渲染时都不会被重置。