網(wǎng)站首頁 編程語言 正文
在 React 16.8 之前,函數(shù)組件也稱為無狀態(tài)組件,因為函數(shù)組件也不能訪問 react 生命周期,也沒有自己的狀態(tài)。react 自 16.8 開始,引入了 Hooks 概念,使得函數(shù)組件中也可以擁有自己的狀態(tài),并且可以模擬對應(yīng)的生命周期。
應(yīng)該在什么時候使用 Hooks 呢?
官方并不建議把原有的 class 組件,大規(guī)模重構(gòu)成 Hooks,而是有一個漸進(jìn)過程:
- 首先,原有的函數(shù)組件如果需要自己的狀態(tài)或者需要訪問生命周期函數(shù),那么用 Hooks 是再好不過了;
- 另外就是,可以先在一些邏輯較簡單的組件上嘗試 Hooks ,在使用起來相對較熟悉,且組內(nèi)人員比較能接受的前提下,再擴(kuò)大 Hooks 的使用范圍。
那么相對于傳統(tǒng)class, Hooks 有哪些優(yōu)勢?
- State Hook 使得組件內(nèi)的狀態(tài)的設(shè)置和更新相對獨立,這樣便于對這些狀態(tài)單獨測試并復(fù)用。
- Hook 將組件中相互關(guān)聯(lián)的部分拆分成更小的函數(shù)(比如設(shè)置訂閱或請求數(shù)據(jù)),而并非強(qiáng)制按照生命周期劃分,這樣使得各個邏輯相對獨立和清晰。
class 生命周期在 Hooks 中的實現(xiàn)
Hooks 組件更接近于實現(xiàn)狀態(tài)同步,而不是響應(yīng)生命周期事件。但是,由于先熟悉的 class 的生命周期,在寫代碼時,難免會受此影響,那么 Hooks 中如何模擬 class 的中的生命周期呢:
總結(jié):
class 組件 | Hooks 組件 |
---|---|
constructor | useState |
getDerivedStateFromProps | useEffect 手動對比 props, 配合 useState 里面 update 函數(shù) |
shouldComponentUpdate | React.memo |
render | 函數(shù)本身 |
componentDidMount | useEffect 第二個參數(shù)為[] |
componentDidUpdate | useEffect 配合useRef |
componentWillUnmount | useEffect 里面返回的函數(shù) |
componentDidCatch | 無 |
getDerivedStateFromError | 無 |
代碼實現(xiàn):
import React, { useState, useEffect, useRef, memo } from 'react';
// 使用 React.memo 實現(xiàn)類似 shouldComponentUpdate 的優(yōu)化, React.memo 只對 props 進(jìn)行淺比較
const UseEffectExample = memo((props) => {
console.log("===== UseStateExample render=======");
// 聲明一個叫 “count” 的 state 變量。
const [count, setCount] = useState(0);
const [count2, setCount2] = useState(0);
const [fatherCount, setFatherCount] = useState(props.fatherCount)
console.log(props);
// 模擬 getDerivedStateFromProps
useEffect(() => {
// props.fatherCount 有更新,才執(zhí)行對應(yīng)的修改,沒有更新執(zhí)行另外的邏輯
if(props.fatherCount == fatherCount ){
console.log("======= 模擬 getDerivedStateFromProps=======");
console.log(props.fatherCount, fatherCount);
}else{
setFatherCount(props.fatherCount);
console.log(props.fatherCount, fatherCount);
}
})
// 模擬DidMount
useEffect(() => {
console.log("=======只渲染一次(相當(dāng)于DidMount)=======");
console.log(count);
}, [])
// 模擬DidUpdate
const mounted = useRef();
useEffect(() => {
console.log(mounted);
if (!mounted.current) {
mounted.current = true;
} else {
console.log("======count 改變時才執(zhí)行(相當(dāng)于DidUpdate)=========");
console.log(count);
}
}, [count])
// 模擬 Didmount和DidUpdate 、 unmount
useEffect(() => {
// 在 componentDidMount,以及 count 更改時 componentDidUpdate 執(zhí)行的內(nèi)容
console.log("======初始化、或者 count 改變時才執(zhí)行(相當(dāng)于Didmount和DidUpdate)=========");
console.log(count);
return () => {
console.log("====unmount=======");
console.log(count);
}
}, [count])
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={() => setCount2(count2 + 1)}>
Click me2
</button>
</div>
);
});
export default UseEffectExample;
注意事項
- useState 只在初始化時執(zhí)行一次,后面不再執(zhí)行;
- useEffect 相當(dāng)于是 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數(shù)的組合,可以通過傳參及其他邏輯,分別模擬這三個生命周期函數(shù);
- useEffect 第二個參數(shù)是一個數(shù)組,如果數(shù)組為空時,則只執(zhí)行一次(相當(dāng)于componentDidMount);如果數(shù)組中有值時,則該值更新時,useEffect 中的函數(shù)才會執(zhí)行;如果沒有第二個參數(shù),則每次render時,useEffect 中的函數(shù)都會執(zhí)行;
- React 保證了每次運行 effect 的同時,DOM 都已經(jīng)更新完畢,也就是說 effect 中的獲取的 state 是最新的,但是需要注意的是,effect 中返回的函數(shù)(其清除函數(shù))中,獲取到的 state 是更新前的。
- 傳遞給 useEffect 的函數(shù)在每次渲染中都會有所不同,這是刻意為之的。事實上這正是可以在 effect 中獲取最新的 count 的值,而不用擔(dān)心其過期的原因。每次我們重新渲染,都會生成新的 effect,替換掉之前的。某種意義上講,effect 更像是渲染結(jié)果的一部分 —— 每個 effect “屬于”一次特定的渲染。
- effect 的清除階段(返回函數(shù))在每次重新渲染時都會執(zhí)行,而不是只在卸載組件的時候執(zhí)行一次。它會在調(diào)用一個新的 effect 之前對前一個 effect 進(jìn)行清理,從而避免了手動去處理一些邏輯 。為了說明這一點,下面按時間列出一個可能會產(chǎn)生的訂閱和取消訂閱操作調(diào)用序列:
function FriendStatus(props) {
// ...
useEffect(() => {
// ...
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// ...
}
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // 運行第一個 effect
// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一個 effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // 運行下一個 effect
// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一個 effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // 運行下一個 effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最后一個 effect
原文鏈接:https://blog.csdn.net/zz130428/article/details/128921417
相關(guān)推薦
- 2022-03-13 .net6引入autofac框架_基礎(chǔ)應(yīng)用
- 2022-11-30 C語言中順序棧和鏈棧的定義和使用詳解_C 語言
- 2022-08-16 C#在MEF框架中實現(xiàn)延遲加載部件_C#教程
- 2022-07-07 Pytorch卷積神經(jīng)網(wǎng)絡(luò)遷移學(xué)習(xí)的目標(biāo)及好處_python
- 2022-04-08 WPF轉(zhuǎn)換器IValueConverter用法_實用技巧
- 2022-02-25 .gitignore 中增加了 .idea/ workspace.xml失效解決方案
- 2024-01-11 org.apache.commons.collections.MapUtils Map集合工具類
- 2022-10-16 Python?numpy中np.random.seed()的詳細(xì)用法實例_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支