React Hooks與setInterval的踩坑問題小結(jié)
一、需求
我們希望有一個(gè)每一秒自動(dòng)+1的定時(shí)器
function Counter() { let [count, setCount] = useState(0); useEffect(() => { let id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]); return <h1>{count}</h1>; }
這種寫法你會(huì)發(fā)現(xiàn)頁面效果確實(shí)能出來,但是性能很差。每當(dāng) count 更改了, useEffect 就會(huì)渲染一次,定時(shí)器也會(huì)不停的被新增與移除。過程如下:
//第一次 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)在我們的需求是在實(shí)現(xiàn)功能的基礎(chǔ)上,還要使得定時(shí)器只監(jiān)聽一次,保障它的性能很高。
二、解決方案
1、函數(shù)式更新
useState 中的set方法可接收函數(shù),該函數(shù)將接收先前的 state ,并返回一個(gè)更新后的值。這樣定時(shí)器每次拿到的是最新的值。
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 返回一個(gè)可變的 ref 對象,返回的 ref 對象在組件的整個(gè)生命周期內(nèi)保持不變。
將定時(shí)器函數(shù)提取出來,每次定時(shí)器觸發(fā)時(shí),都能取到最新到 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>; }
思考:為什么不直接像下面這個(gè)例子,將setInterval
寫成 setInterval(myRef.current, 1000)
這樣呢?為什么要通過一個(gè)函數(shù)返回?
//這個(gè)例子是錯(cuò)誤的 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í)器的第一個(gè)參數(shù)為 interval 變量,如果直接將myRef.current直接賦值給 interval 變量,那么之后的myRef.current的值改變之后,在這里依舊取到的是改變之前的值,因?yàn)?strong>ref的改變不會(huì)引起組件的重新渲染。
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這個(gè)api的設(shè)計(jì)是非常巧妙的。
首先useInterval和setInterval接收的參數(shù)是一樣的,這就降低了我們的學(xué)習(xí)成本
其次,useInterval的delay參數(shù)是可以動(dòng)態(tài)調(diào)整的,而setInterval的delay參數(shù)是沒有辦法動(dòng)態(tài)調(diào)整的
- 當(dāng)
useInterval
Hook 接收到不同 delay,它會(huì)重設(shè) interval。 - 聲明一個(gè)帶有動(dòng)態(tài)調(diào)整 delay 的 interval,來替代寫 添加和清除* interval 的代碼 ——
useInterval
Hook 幫我們做到了**。 - 如果想要暫時(shí)暫停 interval ,那么可以像下面這個(gè)例子一樣
- 當(dāng)
const [delay, setDelay] = useState(1000); const [isRunning, setIsRunning] = useState(true); useInterval(() => { setCount(count + 1); }, isRunning ? delay : null);
- useInterval的delay也可以受控于另外一個(gè)useInterval
function Counter() { const [delay, setDelay] = useState(1000); const [count, setCount] = useState(0); // 增加計(jì)數(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> </> ); }
到此這篇關(guān)于React Hooks與setInterval的踩坑問題小結(jié)的文章就介紹到這了,更多相關(guān)React Hooks與setInterval內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能
這篇文章主要介紹了react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能,大概思路是用一個(gè)div包裹echarts, 然后在echarts的同級(jí)新建一個(gè)div用來用來模擬真實(shí)tooltip,通過鼠標(biāo)移入移出事件控制真實(shí)tooltip的顯示與隱藏,需要的朋友可以參考下2023-05-05簡易的redux?createStore手寫實(shí)現(xiàn)示例
這篇文章主要介紹了簡易的redux?createStore手寫實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10React Native 搭建開發(fā)環(huán)境的方法步驟
本篇文章主要介紹了React Native 搭建開發(fā)環(huán)境的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10React中useEffect與生命周期鉤子函數(shù)的對應(yīng)關(guān)系說明
這篇文章主要介紹了React中useEffect與生命周期鉤子函數(shù)的對應(yīng)關(guān)系說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09react組件的創(chuàng)建與更新實(shí)現(xiàn)流程詳解
React組件分為函數(shù)組件與class組件;函數(shù)組件是無狀態(tài)組件,class稱為類組件;函數(shù)組件只有props,沒有自己的私有數(shù)據(jù)和生命周期函數(shù);class組件有自己私有數(shù)據(jù)(this.state)和生命周期函數(shù)2022-10-10React/Redux應(yīng)用使用Async/Await的方法
本篇文章主要介紹了React/Redux應(yīng)用使用Async/Await的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11React實(shí)現(xiàn)動(dòng)態(tài)調(diào)用的彈框組件
這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)動(dòng)態(tài)調(diào)用的彈框組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式
這篇文章主要介紹了React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01react實(shí)現(xiàn)每隔60s刷新一次接口的示例代碼
本文主要介紹了react實(shí)現(xiàn)每隔60s刷新一次接口的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06