React Hooks與setInterval的踩坑問題小結
一、需求
我們希望有一個每一秒自動+1的定時器
function Counter() {
let [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);
return <h1>{count}</h1>;
}這種寫法你會發(fā)現(xiàn)頁面效果確實能出來,但是性能很差。每當 count 更改了, useEffect 就會渲染一次,定時器也會不停的被新增與移除。過程如下:
//第一次
function Counter() {
//...
useEffect(() => {
let id = setInterval(() => {
setCount(0 + 1);
}, 1000);
return () => clearInterval(id);
}, [0]);
//...
}
//第二次
function Counter() {
//...
useEffect(() => {
let id = setInterval(() => {
setCount(1 + 1);
}, 1000);
return () => clearInterval(id);
}, [1]);
//...
//第N次
}現(xiàn)在我們的需求是在實現(xiàn)功能的基礎上,還要使得定時器只監(jiān)聽一次,保障它的性能很高。
二、解決方案
1、函數(shù)式更新
useState 中的set方法可接收函數(shù),該函數(shù)將接收先前的 state ,并返回一個更新后的值。這樣定時器每次拿到的是最新的值。
function Counter() {
let [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(v => {
return v + 1;
});
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}2、使用useRef
useRef 返回一個可變的 ref 對象,返回的 ref 對象在組件的整個生命周期內(nèi)保持不變。
將定時器函數(shù)提取出來,每次定時器觸發(fā)時,都能取到最新到 count .
function Counter() {
let [count, setCount] = useState(0);
const myRef = useRef(null);
myRef.current = () => {
setCount(count + 1);
};
useEffect(() => {
let id = setInterval(() => {
myRef.current();
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}思考:為什么不直接像下面這個例子,將setInterval寫成 setInterval(myRef.current, 1000)這樣呢?為什么要通過一個函數(shù)返回?
//這個例子是錯誤的
function Counter() {
let [count, setCount] = useState(0);
const myRef = useRef(null);
myRef.current = () => {
setCount(count + 1);
};
useEffect(() => {
let id = setInterval(myRef.current, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}定時器的第一個參數(shù)為 interval 變量,如果直接將myRef.current直接賦值給 interval 變量,那么之后的myRef.current的值改變之后,在這里依舊取到的是改變之前的值,因為ref的改變不會引起組件的重新渲染。
3、用useReducer
將 count 變量存入 reducer 中,使用 useReducer 更新 count
function reducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
default:
throw new Error();
}
}
?
function Counter() {
const [state, dispatch] = useReducer(reducer, 0);
useEffect(() => {
setInterval(() => {
dispatch({ type: "increment" });
}, 1000);
}, []);
return <h1>{state}</h1>;
}4、自定義的hooks
自定義hook:useInterval
import React, { useState, useEffect, useRef } from 'react';
function useInterval(callback, delay) {
const savedCallback = useRef();
// 保存新回調(diào)
useEffect(() => {
savedCallback.current = callback;
});
// 建立 interval
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}使用useInterval:
function Counter() {
let [count, setCount] = useState(0);
useInterval(() => {
// 你自己的代碼
setCount(count + 1);
}, 1000);
return <h1>{count}</h1>;
}useInterval這個api的設計是非常巧妙的。
首先useInterval和setInterval接收的參數(shù)是一樣的,這就降低了我們的學習成本
其次,useInterval的delay參數(shù)是可以動態(tài)調(diào)整的,而setInterval的delay參數(shù)是沒有辦法動態(tài)調(diào)整的
- 當
useIntervalHook 接收到不同 delay,它會重設 interval。 - 聲明一個帶有動態(tài)調(diào)整 delay 的 interval,來替代寫 添加和清除* interval 的代碼 ——
useIntervalHook 幫我們做到了**。 - 如果想要暫時暫停 interval ,那么可以像下面這個例子一樣
- 當
const [delay, setDelay] = useState(1000);
const [isRunning, setIsRunning] = useState(true);
useInterval(() => {
setCount(count + 1);
}, isRunning ? delay : null);- useInterval的delay也可以受控于另外一個useInterval
function Counter() {
const [delay, setDelay] = useState(1000);
const [count, setCount] = useState(0);
// 增加計數(shù)器
useInterval(() => {
setCount(count + 1);
}, delay);
// 每秒加速
useInterval(() => {
if (delay > 10) {
setDelay(delay / 2);
}
}, 1000);
function handleReset() {
setDelay(1000);
}
return (
<>
<h1>Counter: {count}</h1>
<h4>Delay: {delay}</h4>
<button onClick={handleReset}>
Reset delay
</button>
</>
);
}到此這篇關于React Hooks與setInterval的踩坑問題小結的文章就介紹到這了,更多相關React Hooks與setInterval內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
ReactNative點擊事件.bind(this)操作分析
這篇文章主要為大家介紹了ReactNative點擊事件.bind(this)操作分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
React實現(xiàn)歌詞滾動效果(跟隨音樂播放時間滾動)
這篇文章主要為大家詳細介紹了React實現(xiàn)歌詞滾動效果(跟隨音樂播放使勁按滾動),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2024-02-02
React模仿網(wǎng)易云音樂實現(xiàn)一個音樂項目詳解流程
這篇文章主要介紹了React模仿網(wǎng)易云音樂實現(xiàn)一個音樂項目的詳細流程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08

