React Hooks技術最佳實踐(二)

歡迎訪問個人博客html

原文地址:React Hooks技術最佳實踐(二)react

useEffect

useEffect是除useState以外使用最經常使用的Hooks之一,它能夠用來管理反作用,替代傳統class組件中的componentDidMountcomponentWillUnmount方法或是根據依賴項來執行代碼。數組

useEffect的用法並不複雜,可是若是對於它的執行過程和機制不熟悉的話,仍是很容易出現死循環、依賴運算錯誤等問題。同時,它每次運行都至關於對當前的狀態存儲了一份快照,這就這意味在它當中執行setTimeout獲取到的都不是最新的狀態,而是運行時的狀態。想要更加深刻了解的推薦閱讀useEffect 完整指南瀏覽器

本文章屬於React Hooks技術最佳實踐的第二篇文章,第一篇介紹了useState的最佳實踐,感興趣的朋友能夠閱讀React Hooks技術最佳實踐(一)異步

替代componentDidMount

經過設置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])
複製代碼

依賴函數

除了使用stateprops做爲依賴項,函數也是能夠直接做爲依賴項使用。不過因爲函數每次在組件渲染時都會從新執行,因此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只在值改變後才執行。

這一部分在以前的文章中有介紹,這裏就不具體展開了。

Memoization技術在React中的應用

useLayoutEffect

大多數狀況下使用useEffect便可,不過當你須要在useEffect中操做DOM時,爲了優化渲染效果,可使用useLayoutEffect。它會在DOM更新完成以後再執行,同時能夠讀取到DOM佈局並同步觸發渲染,以後瀏覽器才進行繪製。

下一篇介紹其餘的Hooks的用法與技巧。

官方的FAQ其實寫的很不錯,不少陷阱和技巧都介紹了,推薦閱讀。

參考文章

  1. useEffect 完整指南
相關文章
相關標籤/搜索