useEffect使用指南


React項目中如果使用函數式組件進行開發時,如果想在不使用class組件的情況下使用state和其他React特性,Hook將是你的不二選擇。

而Effect Hook又是一種比較常見的Hook,可以在函數式組件中執行副作用操作。剛開始可以理解為created、update生命周期。

一、為effect添加依賴

    const [count, setCount] = useState(0)

    useEffect(() => {
        const fn = setTimeout(() => {
            console.log(count)
            setCount(count+1)。
        }, 1000)
    })
    // 0
    // 1
    // 2

    useEffect(() => {
        const fn = setTimeout(() => {
            console.log(count)
            setCount(count+1)
        }, 1000)
    },[])
    // 0

effect的執行機制,是比較兩次依賴項是否相同,不同則執行相關effect。

如果不替effect添加依賴,則是比較兩次effect。而我們知道在函數式組件中,每次渲染的state、effect都是不同的。只要組件內部狀態發生變化就會執行effect。

而將依賴項設置為一個固定值,則effect只會初始化時執行一次。

二、effect內部狀態自調用

上面兩種情況是比較簡單的,在復雜的業務邏輯中,我們往往需要依據某個狀態的變化然后再來執行副作用操作,並且還需要為effect設置清除函數。我們看看下面這段代碼:

    useEffect(() => {
        const fn = setTimeout(() => {
            console.log(count)
            setCount(count+1)
        }, 1000)
        return () => clearTimeout(fn)
    },[count])
    // 0
    // 1
    // 2

我們在effect中添加了清除函數,並且需要將count作為依賴在count變化時執行內部代碼。這應該是大家使用Hook Effect比較常見的用法。

但是就這個例子而言,這種設置依賴的寫法是有邏輯漏洞的。在effect內部只需要將count做遞增,而此時React是知道count的值,所以effect並不要使用count,可以使用setState的函數形式代替傳參。

    useEffect(() => {
        const fn = setTimeout(() => {
            console.log(count)
            setCount(c => c+1)
        }, 1000)
        return () => clearTimeout(fn)
    })
    // 0
    // 1
    // 2

setCount(c => c+1)就是在方法內部發送更新指令,告訴React去更新state的值,這樣就將count從effect依賴中去掉,保持內部邏輯干凈無污染。

三、將函數放在effect內部

有時一個effect內部函數代碼量過多,我們可以將代碼進行分割單獨封裝成函數,使整個邏輯看起來清晰明了。可能我們為了進一步追求代碼規范,需要將函數抽離放在effect外部。

    const countPlayer = () => {
        return setTimeout(() => {
            setCount(count+10)
        },1000)
    }

    useEffect(() => {
       const fn = countPlayer()
       return () => clearTimeout(fn)
    },[])
    console.log(count)
    // 0
    // 10

比如我們要在初始化時通過接口獲取數據進行setState賦值然后渲染UI,並且獲取數據操作只需要執行一次。上面的代碼顯示count為0時執行了effect副作用,通過countPlayer函數進行組件state初始化操作。第二次由於依賴沒有變更就跳過了直接往后執行。

邏輯上看起來沒問題,但是如果當代碼量過大並且存在多個函數嵌套依賴的情況,就很難保證准確更新相關effect依賴。並且如果項目安裝了eslint-plugin-react-hooks,在項目打包運行時就會出現警告。

 這種情況,我們將函數移動到effect內部就能很好地規避這個問題。

四、將函數放在effect外部

如果一個函數在多個effect中被復用,這時我們就必須將函數放在effect外部了。至於邏輯漏洞和代碼eslint告警的問題,一般有兩種方法解決:

  • 將函數轉化成純函數,移動到組件外部;
  • 使用useCallback Hook進行包裝;

如果一個函數沒有使用組件內部任何state、props就可以轉化成純函數,在不同的effect中任意調用。

如果函數內部需要對state等進行setState操作,可以使用useCallback將整個函數包裝成一個依賴state的可變值,然后作為effect的依賴使用。

可能有的小伙伴會問,為啥不直接將countPlayer函數作為依賴呢?下面我們看看這樣設置后的運行情況。

    const countPlayer = () => {
        return setTimeout(() => {
            setCount(count+10)
        },1000)
    }

    useEffect(() => {
       const fn = countPlayer()
       return () => clearTimeout(fn)
    },[countPlayer])
    console.log(count)
    // 0
    // 10
    // 20
    // ...

會出現無限循環自調用的bug。之前我們提過,在函數式組件內部,每次獲取的函數體、state、effect都不一樣,所以會導致effect陷入死循環。下面我們將函數用useCallback進行包裝看看效果。

    const countPlayer = useCallback(() => {
        return setTimeout(() => {
            setCount(c => c+10)
        },1000)
    },[])

    useEffect(() => {
       const fn = countPlayer()
       return () => clearTimeout(fn)
    },[countPlayer])
    console.log(count)
    // 0
    // 10

這樣既滿足了將函數體抽離出effect的目的;並且通過useCallback包裝使函數體本身成為了一個擁有獨立依賴的可變值,可以在不同的effect之間復用。

至此,effect Hook一些常用的思路和使用技巧都講解完畢!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM