## Use setinterval in React ### - 20241010 最新更新修復了精度差問題 [Use setinterval 忽略的精度差問題](https://hackmd.io/@nick-huang-1996/S1Rx0sMk1g) --- 最近工作上在寫`Timer`相關功能,有需要在 React 中,使用`setinterval`,但寫完後有發現到計時器好像會停留在倒數一秒時停住了,覺得哪裡怪怪的。 後續問了查了下資料跟同事詢問才發現不得了,這是因為在 React 中,每次組件重新渲染時,所有的變量和函數都會重新定義。當你在 useEffect 中設置 setInterval 時,這個 setInterval 函數會捕捉到當前渲染時的 seconds 值,這也包括 setSeconds 函數的閉包。 #### 問題原因 > 在 useEffect 的第一次執行中,setInterval 捕捉了初始渲染時的 seconds,這可能是 0,所以定時器沒有正確運行。即使 seconds 更新,定時器回調函數仍然使用的是最初的閉包值。 #### 簡單調整的作法 * 你可以通過依賴更新來解決這個問題。這意味著讓 useEffect 中的 setInterval 每次都能捕捉到最新的 seconds 值。可以將 seconds 放入 useEffect 的依賴 [] 中,這樣每次 seconds 更新時,useEffect 都會重新執行並設置一個新的定時器。 --- 附上 Poo Poo Code > 一開始停住時沒有反應時,在想是不是有觸碰到什麼奇怪機制,還特地多寫了些倒數 -1 之類的判斷繞過去,現在想想還挺蠢的 ```javascript import React, { useState, useEffect, useRef } from "react"; const CountdownTimer = ({ initialSeconds }) => { const originSeconds = initialSeconds; const [seconds, setSeconds] = useState(initialSeconds); const [status, setStatus] = useState(false); const timerId = useRef(null); useEffect(() => { if (!status) return; timerId.current = setInterval(() => { setSeconds((prevSeconds) => { if (prevSeconds === 0) { clearInterval(timerId.current); stop(); return 0; } return prevSeconds - 1; }); }, 1000); return () => clearInterval(timerId.current); }, [status]); useEffect(() => { if (seconds === 0 && status) { console.log("時間到!"); } }, [seconds, status]); const start = () => { if (seconds > 0) setStatus(true); }; const stop = () => { setStatus(false); clearInterval(timerId.current); }; const reset = () => { stop(); setSeconds(originSeconds); }; return ( <div> <h1>剩餘時間:{seconds} 秒</h1> <button onClick={start}>Start</button> <button onClick={stop}>Stop</button> <button onClick={reset}>Reset</button> </div> ); }; const App = () => { return <CountdownTimer initialSeconds={5} />; }; export default App; ``` #### 延伸議題 * [Making setInterval Declarative with React Hooks](https://overreacted.io/making-setinterval-declarative-with-react-hooks) - 封裝成Hooks形式的 `useInterval` * [倒數計時器的性能優化之路](https://joouis.com/2018/optimization-road-of-count-down-timer/) - 根據裝置更新幀率,每秒 diff 時間戳確認刷新精度
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up