React(五)useEffect、useRef、useImperativeHandle、useLayoutEffect

(一)useEffect

useEffect – React 中文文檔

useEffect hook用于模擬以前的class組件的生命周期,但比原本的生命周期有著更強大的功能

1.類組件的生命周期

在類組件編程時,網絡請求,訂閱等操作都是在生命周期中完成

import React, { Component } from 'react'export default class App extends Component {// 組件掛載后執行componentDidMount(){// 發送請求// 事件總線綁定// 創建定時器等}// 組件更新后執行componentDidUpdate(){}// 組件銷毀前執行componentWillUnmount(){// 事件總線解綁// 清除定時器}render() {return (<div>App</div>

2.函數式組件的生命周期

函數式組件沒有明確的生命周期,使用useEffect來模擬生命周期

useEffect(setup, dependencies?)

在useRffect的第一個參數傳入回調函數,執行請求、掛載等操作

useEffect會在組件每次掛載、更新后調用回調?

import { useState, useEffect } from 'react'
function App() {const [count, setCount] = useState(0)useEffect(()=>{// 發送請求// store倉庫訂閱subscribe// 事件總線綁定 eventbus.on// 操作外部domdocument.title = count})return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button></>)
}

如何在組件銷毀前取消訂閱或者移除綁定?

只需要在第一個回調里返回一個回調函數即可,useEffect會在組件銷毀前/組件更新前調用

useEffect(()=>{// 發送請求// store倉庫訂閱subscribe// 事件總線綁定 eventbus.on// 操作外部domdocument.title = count// 計數器const time = setInterval(()=>{console.log(1);},1000)return ()=>{// store倉庫取消訂閱 unsubscribe// 清除事件總線// 清除計數器等操作clearInterval(time)}})

回調函數內的代碼太長了

拆分useEffect,每個功能都可以單獨寫一個useEffect,react會自動處理

  useEffect(()=>{// 發送請求})useEffect(()=>{// store倉庫訂閱subscribereturn ()=>{// store倉庫取消訂閱 unsubscribe}})useEffect(()=>{// 計數器const time = setInterval(()=>{console.log(count);},1000)return ()=>{// 清除計數器等操作clearInterval(time)}})

執行次數會不會太多了?

向上面那樣書寫的話,每次update都會執行回調,更新一次dom就請求一次、綁定一次事件這樣子也太蠢了,因此useEffect可以傳入第二個參數,用來控制依據什么來決定是否執行,和之前useCallback、useMemo一樣,都會傳入dependencies這個參數

  // 只執行一次useEffect(()=>{// 發送請求},[])// 只執行一次useEffect(()=>{// store倉庫訂閱subscribereturn ()=>{// store倉庫取消訂閱 unsubscribe}},[])// count改變才執行useEffect(()=>{document.title = count},[count])

useEffect先簡單寫到這里,useEffect雖然是模擬生命周期,但它能做的事比生命周期更多,能夠根據傳入的數組參數判斷是否執行?

(二)useRef

useRef – React 中文文檔

useRef 是一個 React Hook,它能幫助引用一個不需要渲染的值

useRef(initialValue)

initialValue:ref 對象的?current?屬性的初始值。可以是任意類型的值。這個參數在首次渲染后被忽略?

useRef hook主要有兩個功能:

  • 綁定dom元素
  • 保存一個數據,在整個生命周期中可以保存不變?

1.綁定dom元素

初始化const xxx = useRef();通過ref={xxx}來綁定ref

import { useState, useRef } from 'react'
function App() {const [count, setCount] = useState(0)const nameRef = useRef()console.log(nameRef.current);return (<><div ref={nameRef}>csq</div><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button></>)
}

通過xxx.current獲取該dom元素

2.綁定一個值(解決閉包陷阱)

先說說閉包陷阱

閉包陷阱是指使用react hooks的時候,由于閉包特性,在某些函數內獲取useState或者props的值時獲取到的是舊的值,而實際值已經改變

使用 ref 可以確保:

  • 可以在重新渲染之間?存儲信息(普通對象存儲的值每次渲染都會重置)。
  • 改變它?不會觸發重新渲染(狀態變量會觸發重新渲染)。
  • 對于組件的每個副本而言,這些信息都是本地的(外部變量則是共享的)。

改變 ref 不會觸發重新渲染,所以 ref 不適合用于存儲期望顯示在屏幕上的信息。如有需要,使用 state 代替。

將新增count的操作放到useCallback回調里,會導致讀取到的count始終為0

const [count, setCount] = useState(0)const increment = useCallback(()=>{setCount(count+1) // set(0+1)console.log(count); // 0},[])return (<><div>{count}</div><button onClick={()=>increment()}>加1</button></>)

因為useCallback傳入的依賴為空,意味著increment函數只生成一次,只能讀取到生成時count的狀態,即0(我感覺我也是蒙的)

這樣就形成了閉包陷阱

解決辦法:

(1)添加useCallback的依賴即可

const increment = useCallback(()=>{setCount(count+1) console.log(count)},[count])

(2)使用useRef

const [count, setCount] = useState(0)const countRef = useRef()// count改變會引起重新渲染,這樣countRef的值每次都和count相等countRef.current = countconst increment = useCallback(()=>{setCount(countRef.current+1) },[])return (<><div>{count}</div><button onClick={()=>increment()}>加1</button></>)

這里肯定不是應用useRef的最好場景,畢竟加個依賴項就解決了

但使用useRef的話,increment函數就不會重新加載了!

(三)useImperativeHandle

useImperativeHandle – React 中文文檔

useImperativeHandle 是 React 中的一個 Hook,它能讓你自定義由 ref 暴露出來的句柄。

useImperativeHandle(ref, createHandle, dependencies?)

1.在父組件使用子組件的ref?

子組件獲取父組件ref的方法:forwardRef()

forwardRef – React 中文文檔

import { useRef, memo, forwardRef, useImperativeHandle } from 'react'
function App() {const childrenRef = useRef()const getDom = ()=>{console.log(childrenRef.current);}return (<><button onClick={getDom}>獲取dom元素</button><Children ref={childrenRef}></Children></>)
}const Children = memo(forwardRef(function(props,ref){return (<><input type="text" ref={ref} /></>)
}))

2.通過useImperativeHandle hook控制子組件ref能暴露的部分

import { useRef, memo, forwardRef, useImperativeHandle } from 'react'
function App() {const childrenRef = useRef()const getDom = ()=>{console.log(childrenRef.current);childrenRef.current.set(100)childrenRef.current.focus()}return (<><button onClick={getDom}>獲取dom元素</button><Children ref={childrenRef}></Children></>)
}const Children = memo(forwardRef(function(props,ref){const inputRef = useRef()useImperativeHandle(ref,()=>{// 返回對象 該對象就是父組件能操作的chilrenRefreturn {set(value){inputRef.current.value = value},focus(){inputRef.current.focus()},}})return (<><input type="text" ref={inputRef}  /></>)
}))

?這個hook的使用不是很常見,只要了解就ok

(四)useLayoutEffect

useLayoutEffect – React 中文文檔

useLayoutEffect 是 useEffect 的一個版本,在瀏覽器重新繪制屏幕之前觸發。

useLayoutEffect(setup, dependencies?)

useLayoutEffect和useEffect在各個方面都是相同的,只是執行的時期不同,useLayout會阻塞dom的更新。如果需要在dom更新前進行操作,使用useLayoutEffect

1.使用useEffect

function App() {const [count,setCount] = useState(0)// 可見count在點擊重置之后會先閃回0再變為隨機數useEffect(()=>{console.log('useEffect');if(count == 0){setCount(Math.random()+100)}})return (<><h1>count:{count}</h1><button onClick={()=>setCount(0)}>重置為0</button></>)
}

2.使用useLayoutEffect

如果需要在dom渲染之前改變的需求就使用useLayoutEffect?

function App() {const [count,setCount] = useState(0)// 在dom重新渲染前就捕獲count進行更新 不會出現閃動情況useLayoutEffect(()=>{console.log('useLayoutEffect');if(count == 0){setCount(Math.random()+100)}})return (<><h1>count:{count}</h1><button onClick={()=>setCount(0)}>重置為0</button></>)
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/22704.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/22704.shtml
英文地址,請注明出處:http://en.pswp.cn/web/22704.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法題day37日(補5.23日卡:貪心算法day4)

一、刷題&#xff1a; 1.leetcode題目 860. 檸檬水找零 - 力扣&#xff08;LeetCode&#xff09;&#xff08;easy&#xff09;&#xff1a; 我覺得我寫的代碼有點蠢 class Solution:def lemonadeChange(self, bills: List[int]) -> bool:dict_ {5:0,10:0}if bills[0] !…

Python降維基礎知識:深入探索與實戰應用

Python降維基礎知識&#xff1a;深入探索與實戰應用 在數據分析和機器學習的廣闊領域中&#xff0c;降維技術一直扮演著重要的角色。Python&#xff0c;作為數據處理和機器學習的首選語言&#xff0c;為我們提供了豐富的降維工具和算法。本文將從四個方面、五個方面、六個方面…

算法訓練營第四十九天 | LeetCode 139單詞拆分

LeetCode 139 單詞拆分 基本還是完全背包的思路&#xff0c;不過用了三重循環&#xff0c;第三重循環是用于判斷當前字符串尾部指定長度字符是否和列表中某一字符串相同&#xff0c;是的話可以將當前dp[j]或上當前下標減去該單詞長度后的下標值。 代碼如下&#xff1a; clas…

平滑值(pinghua)

平滑值 題目描述 一個數組的“平滑值”定義為&#xff1a;相鄰兩數差的絕對值的最大值。 具體的&#xff0c;數組a的平滑值定義為 f ( a ) m a x i 1 n ? 1 ∣ a i 1 ? a i ∣ f(a)max_{i1}^{n-1}|a_{i1}-a_i| f(a)maxi1n?1?∣ai1??ai?∣ 現在小紅拿到了一個數組…

【前端】響應式布局筆記——flex

二、Flex Flex(FlexiableBox:彈性盒子&#xff0c;用于彈性布局&#xff0c;配合rem處理尺寸的適配問題)。 1、flex-direction:子元素在父元素盒子中的排列方式。 父級元素添加&#xff1a;flex-direction: row; 父級元素添加&#xff1a;flex-direction: row-reverse; 父…

家政預約小程序13我的訂單

目錄 1 我的訂單頁面布局2 全部訂單頁面3 完善訂單狀態4 查詢訂單信息總結 現在我們已經完成了家政預約小程序主體功能的開發&#xff0c;包含服務的查看&#xff0c;在線預約已經登錄等功能。預約之后就需要家政公司的客服進行派單&#xff0c;由服務人員進行上門服務。在小程…

Hotcoin精彩亮相Consensus 2024 Austin,探索行業風向標

5 月 31 日&#xff0c;由CoinDesk主辦的“Consensus 2024”大會在德克薩斯州的奧斯汀市正式落下帷幕。作為全球規模最大、最具影響力的加密貨幣、區塊鏈、Web3盛會&#xff0c;本次Consensus 2024 Austin吸引來自 100 多個國家/地區的 15,000 多名與會者、6,800 家公司、850 多…

Linux 程序守護腳本

引言 程序是由代碼形成的&#xff0c;代碼是由人寫的。只要是人&#xff0c;都會有疏忽的時候&#xff0c;導致寫出的程序有bug&#xff0c;當然最嚴重的bug就是程序閃退。 本文旨在提供一個程序守護腳本&#xff0c;當監測到程序閃退后&#xff0c;立馬將程序再起啟動&#…

java 獲取文件的MIME類型

MIME類型簡介 MIME類型&#xff08;Multipurpose Internet Mail Extensions&#xff09;是一種標準&#xff0c;用于描述文檔、文件或字節流的性質和格式。它最初是為了在電子郵件中傳輸多媒體數據而設計的&#xff0c;但后來也被廣泛用于Web開發中。每種MIME類型都由一個唯一…

【C++】手動模擬String底層與深淺拷貝

在string類&#xff1a;版本、組件、構造、操作及應用和 C中string的一些超常用函數 (附習題)這兩篇文章中我們已經了解到了string&#xff0c;現在讓我們再來手動實現模擬一下吧~ 模擬實現string是為了更好的理解string函數的使用和深淺拷貝方面的知識~ 總體整理了兩張思維導…

【Python編程】【Jupyter Notebook】啟動時報錯:no available port could be found

一、報錯描述 在Jupyter Notebook中編寫程序&#xff0c;無法運行&#xff0c;提示由于沒有可供監聽的端口&#xff0c;無法啟動Jupyter服務器&#xff0c;如下圖所示&#xff1a; 二、原因分析 通過報錯信息&#xff0c;猜測大概是由于網絡環境的原因。首先&#xff0c;關閉…

多角度剖析事務和事件的區別

事務和事件這兩個概念在不同的領域有著不同的含義&#xff0c;尤其是在計算機科學、數據庫管理和軟件工程中。下面從多個角度來剖析事務和事件的區別&#xff1a; 計算機科學與數據庫管理中的事務 事務(Transaction)&#xff1a; 定義&#xff1a;在數據庫管理中&#xff0c…

C語言(結構體)

Hi~&#xff01;這里是奮斗的小羊&#xff0c;很榮幸各位能閱讀我的文章&#xff0c;誠請評論指點&#xff0c;歡迎歡迎~~ &#x1f4a5;個人主頁&#xff1a;小羊在奮斗 &#x1f4a5;所屬專欄&#xff1a;C語言 本系列文章為個人學習筆記&#xff0c;在這里撰寫成文一…

Java項目之消息隊列(手寫java模擬實現mq)【五、內存存儲數據,方便快速拿到數據對象】? ★

九. 內存數據結構設計 硬盤上存儲數據, 只是為了實現 “持久化” 這樣的效果. 但是實際的消息存儲/轉發, 還是主要靠內存的結 構. 對于 MQ 來說, 內存部分是更關鍵的, 內存速度更快, 可以達成更?的并發 創建 MemoryDataCenter 創建 mqserver.datacenter.MemoryDataCenter …

AIGC實戰!7個超熱門的 Midjourney 關鍵詞教程

一、剪紙風格 核心詞&#xff1a; paper art&#xff08;剪紙藝術&#xff09; 關鍵技巧&#xff1a; 主體物&#xff1a;可以換成任意主角&#xff0c;Chinese illustration &#xff08;中國風插畫&#xff09;&#xff1b;藝術風格&#xff1a;paper art &#xff08;剪紙…

ruoyi vue 集成積木報表真實記錄

按官方文檔集成即可 積木報表官方集成文檔 集成問題 1.注意 idea 配置的 maven 需要設置成 本地配置&#xff0c;不可以使用 idea 自帶的 maven,自帶 maven 會導致私有源調用不到 后端代碼 新建 base 模塊 maven配置 <project xmlns"http://maven.apache.org/POM/…

微軟云計算[3]之Windows Azure AppFabric

Windows Azure AppFabric AppFabric概述AppFabric關鍵技術服務總線訪問控制高速緩存 AppFabric概述 AppFabric為本地應用和云中應用提供了分布式的基礎架構服務 用戶本地應用與云應用之間進行安全聯接和信息傳遞 云應用和現有應用或服務之間的連接及跨語言、跨平臺、跨不同標…

模擬藍牙打卡機

模擬藍牙打卡&#xff0c;源碼來自github項目dingBLE 只需要一個ESP32模塊模擬藍牙打卡機&#xff0c;即可通過藍牙打卡 親測有效arduino代碼如下 打卡機的MAC和RAW數據可使用安卓app mRFconnect 掃描獲取 #include "BLEDevice.h" #include "BLEUtils.h"…

視覺SLAM

二、視覺SLAM十四講&#xff1a;從理論到實踐 第二版 電子版PDF 鏈接&#xff1a;https://pan.baidu.com/s/1VsrueNrdqmzTvh-IlFBr9Q 提取碼&#xff1a;vfhe 源碼 Gitee鏈接&#xff1a;https://gitee.com/gnef233/slambook2.git SLAM領域超實用開源方案匯總一

C++數據結構之:哈希表Hash

摘要&#xff1a; it人員無論是使用哪種高級語言開發東東&#xff0c;想要更高效有層次的開發程序的話都躲不開三件套&#xff1a;數據結構&#xff0c;算法和設計模式。數據結構是相互之間存在一種或多種特定關系的數據元素的集合&#xff0c;即帶“結構”的數據元素的集合&am…