其實關於這個問題在知乎和百度上都有說起,可是在掘金上卻沒有詳細的文章,所以準備出一篇文章來解決在useEffect中如何使用定時器。有一篇文章寫得特別好,若是你們想深刻理解能夠點擊此連接 使用 React Hooks 聲明 setIntervalcss
首先讓咱們作一個小Demo,設置一個名爲value的state,並每隔5秒,產生一個隨機數,並讓value加上這個數react
import React, { useState, useEffect } from 'react';
import './App.css';
function App() {
const [value, setValue] = useState<number>(0);
useEffect(() => {
const timer: NodeJS.Timeout = setInterval(() => {
const random = (Math.random() * 10) | 0;
setValue(value + random);
}, 5000);
return () => {
clearInterval(timer);
};
}, []);
return <div>{value}</div>;
}
export default App;
複製代碼
你自信滿滿的打開了瀏覽器,too simple! 忽然發現了value的值只發生了一次變化,你檢查發現,原來useEffect的依賴性並無填入,因而你當心翼翼的將value填入依賴性數組
useEffect(() => {
// .....
}, [value]);
複製代碼
perfect!,代碼如期執行,每隔5秒都會增長一個值,內心想着‘不愧是我!’瀏覽器
若是咱們在代碼中加入一個定時器數組,用來記錄你添加了多少個定時器閉包
function App() {
const [value, setValue] = useState<number>(0);
const [timers, setTimers] = useState<Array<NodeJS.Timeout>>([]);
useEffect(() => {
const timer: NodeJS.Timeout = setInterval(() => {
const random = (Math.random() * 10) | 0;
setValue(value + random);
}, 5000);
timers.push(timer);
setTimers(timers);
console.log(timers);
return () => {
clearInterval(timer);
};
}, [value]);
return <div>{value}</div>;
}
複製代碼
你會驚訝的發現你不只value發生變化了,並且又多生成了一個定時器。以下圖所示。 dom
這對於瀏覽器性能來講是絕對不能夠接受的,那這是爲何產生的呢, 產生的緣由是由於useEffect 在第一次渲染時獲取值爲 0 的 value,將再也不次執行 effect,因此 setInterval 一直引用第一次渲染時的閉包 value,所以每次都是在0的基礎上添加一個隨機數,而不是依次累加,最關鍵的是若是你開啓了Vscode的eslint插件,它還會給你自動補齊依賴項, 這裏推薦一篇關於react-hooks閉包問題的文章 陳舊閉包問題,解釋的比較清楚。爲了解決這個問題,咱們須要引入react中另一個hook ,useRef。useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化爲傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命週期內保持不變,利用這個特性,咱們把它用在咱們的demo中看看效果性能
function App() {
const [value, setValue] = useState<number>(0);
const [timers, setTimers] = useState<Array<NodeJS.Timeout>>([]);
const saveCallBack: any = useRef();
const callBack = () => {
const random: number = (Math.random() * 10) | 0;
setValue(value + random);
};
useEffect(() => {
saveCallBack.current = callBack;
return () => {};
});
useEffect(() => {
const tick = () => {
saveCallBack.current();
};
const timer: NodeJS.Timeout = setInterval(tick, 5000);
timers.push(timer);
setTimers(timers);
console.log(timers);
return () => {
clearInterval(timer);
};
}, []);
return <div>{value}</div>;
}
複製代碼
使用了useRef後,定時器不會被重複建立,可是value的值變成了依次累加,達到了預期的效果,真是讓人神清氣爽,so cool!spa