歡迎訪問個人博客html
原文地址:React Hooks技術最佳實踐(二)react
useEffect
是除useState
以外使用最經常使用的Hooks之一,它能夠用來管理反作用,替代傳統class組件中的componentDidMount
和componentWillUnmount
方法或是根據依賴項來執行代碼。數組
useEffect
的用法並不複雜,可是若是對於它的執行過程和機制不熟悉的話,仍是很容易出現死循環、依賴運算錯誤等問題。同時,它每次運行都至關於對當前的狀態存儲了一份快照,這就這意味在它當中執行setTimeout
獲取到的都不是最新的狀態,而是運行時的狀態。想要更加深刻了解的推薦閱讀useEffect 完整指南。瀏覽器
本文章屬於React Hooks技術最佳實踐的第二篇文章,第一篇介紹了useState
的最佳實踐,感興趣的朋友能夠閱讀React Hooks技術最佳實踐(一)。異步
經過設置useEffect
的第二個依賴參數爲[]
能夠替代componentDidMount
,雖然它們的執行時機不是徹底一致的。這樣咱們就能夠在useEffect
中處理須要在組件掛載後的操做或者異步請求,它可以獲取到state
。由於依賴項爲[]
,因此在整個組件生命週期中只會在掛載時運行。async
useEffect(() => {
// 在組件掛載後處理事務或反作用
// 可以獲取到state
}, [])
複製代碼
先來看看如何正確的使用async
方法。ide
錯誤的示範函數
useEffect(async () => {
const { data, result } = await asyncRequest();
}, [])
複製代碼
useEffect
並不建議你在回調函數中直接使用async
,這樣使用會直接觸發報錯,一般若是安裝了相關lint的話會有提示。由於這麼使用會致使回調函數相互之間產生競爭狀態,而Effect回調函數應該是同步的。佈局
推薦的作法post
const requestFn = async () => {
const { data, result } = await asyncRequest();
}
useEffect(() => {
requestFn();
}, [])
複製代碼
若是須要在獲取異步數據以後更新state
,也能夠在requestFn
函數中處理。
const [value, updateValue] = useState();
const requestFn = async () => {
const { data, result } = await asyncRequest();
updateValue(value);
}
useEffect(() => {
requestFn();
}, [])
複製代碼
若是異步請求依賴任何的state
,則須要清晰的寫在useEffect
的依賴項中,這樣每次在state
改變以後都會執行異步請求。
const requestFn = async (param) => {
const { data, result } = await asyncRequest(param);
updateValue(value);
}
useEffect(() => {
requestFn(param);
}, [param])
複製代碼
咱們知道每次state
或者props
改變都會致使組件的re-render,因此useEffect
在沒有任何依賴項時每次都會執行一遍。這時若是在它當中改變了state
,那麼就會致使死循環。過程就是執行useEffect
改變了state
,而改變state
又致使了重複執行useEffect
。
錯誤的寫法
const [count, updateCount] = useState(0);
useEffect(() => {
updateCount(prevCount => prevCount++);
})
複製代碼
正確的寫法一
const [count, updateCount] = useState(0);
// 增長前置條件,知足時才執行更新狀態
useEffect(() => {
if (count < 1) {
updateCount(prevCount => prevCount++);
}
})
複製代碼
正確的寫法二
const [count, updateCount] = useState(0);
const [num, updateNum] = useState(1)
// 依賴num,每次num改變後纔會執行Effect
useEffect(() => {
updateCount(prevCount => prevCount+num);
}, [num])
複製代碼
除了使用state
、props
做爲依賴項,函數也是能夠直接做爲依賴項使用。不過因爲函數每次在組件渲染時都會從新執行,因此Effect會不必的重複執行。
不過可使用useCallback
方法來避免這種狀況。
每次渲染都重複執行Effect
const doSomething = () => {}
useEffect(() => {
// do somethings
}, [doSomething])
複製代碼
使用useCallback
const doSomething = () => useCallback(() => {}, [])
useEffect(() => {
// do somethings once
}, [doSomething])
複製代碼
若是函數依賴任何其餘的狀態執行,則能夠將依賴加入到useCallback
的依賴數組項中,這樣在依賴項改變後函數都會從新執行,Effect因爲依賴了函數,因此Effect也會執行。
正確的使用Effect的依賴是很是重要的,每次依賴改變後都會使用Object.is
方法來比較,若是不一樣,則執行Effect。因此咱們能夠經過Memoization技術來優化,具體來講就是每次狀態改變後都會與以前記憶的狀態做對比,若是值發生了改變,則返回新的狀態,並記憶,若是值沒有改變,只是引用變了,則返回記憶的狀態。這樣Effect只在值改變後才執行。
這一部分在以前的文章中有介紹,這裏就不具體展開了。
大多數狀況下使用useEffect
便可,不過當你須要在useEffect
中操做DOM時,爲了優化渲染效果,可使用useLayoutEffect
。它會在DOM更新完成以後再執行,同時能夠讀取到DOM佈局並同步觸發渲染,以後瀏覽器才進行繪製。
下一篇介紹其餘的Hooks的用法與技巧。
官方的FAQ其實寫的很不錯,不少陷阱和技巧都介紹了,推薦閱讀。