網站首頁 編程語言 正文
一、需求
我們希望有一個每一秒自動+1的定時器
function Counter() { let [count, setCount] = useState(0); useEffect(() => { let id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]); return <h1>{count}</h1>; }
這種寫法你會發現頁面效果確實能出來,但是性能很差。每當 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次 }
現在我們的需求是在實現功能的基礎上,還要使得定時器只監聽一次,保障它的性能很高。
二、解決方案
1、函數式更新
useState 中的set方法可接收函數,該函數將接收先前的 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 對象在組件的整個生命周期內保持不變。
將定時器函數提取出來,每次定時器觸發時,都能取到最新到 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)
這樣呢?為什么要通過一個函數返回?
//這個例子是錯誤的 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>; }
定時器的第一個參數為 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(); // 保存新回調 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接收的參數是一樣的,這就降低了我們的學習成本
-
其次,useInterval的delay參數是可以動態調整的,而setInterval的delay參數是沒有辦法動態調整的
- 當
useInterval
Hook 接收到不同 delay,它會重設 interval。 - 聲明一個帶有動態調整 delay 的 interval,來替代寫 添加和清除* interval 的代碼 ——
useInterval
Hook 幫我們做到了**。 - 如果想要暫時暫停 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); // 增加計數器 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> </> ); }
原文鏈接:https://juejin.cn/post/7090822385430953992
相關推薦
- 2022-11-11 Python?第三方庫?Pandas?數據分析教程_python
- 2023-07-07 React中useState的setState方法請求了好多次
- 2022-07-02 C語言詳細講解指針數組的用法_C 語言
- 2022-03-12 一文搞懂MemoryCache?清除全部緩存的方法_實用技巧
- 2021-12-06 Android優化提升應用啟動速度及Splash頁面的設計_Android
- 2022-07-19 react props的特點
- 2022-10-15 C語言const關鍵字的用法詳解_C 語言
- 2022-09-09 Redis中ZSet的具體使用_Redis
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支