React Hooks系列之useEffect

前言

react hooks 是 React 16.8 的新增特性。 它可以讓我們在函數組件中使用 state 、生命週期以及其他 react特性,而不僅限於 class 組件。react hooks 的出現,標示着 react中不會在存在無狀態組件了,只有類組件和函數組件。具體可查看官網

優勢:

  1. 函數組件不能使用state,遇到交互更改狀態等複雜邏輯時不能更好地支持,hooks讓函數組件更靠近class組件,擁抱函數式編程。
  2. 解決副作⽤問題,hooks出現可以處理數據獲取、訂閱、定時執行任務、手動修改 ReactDOM這些⾏爲副作用,進行副作用邏輯。比如useEffect。
  3. 更好寫出有狀態的邏輯重用組件。
  4. 讓複雜邏輯簡單化,比如狀態管理:useReducer、useContext。
  5. 函數式組件比class組件簡潔,開發的體驗更好,效率更⾼,性能更好。
  6. 更容易發現無用的狀態和函數。

useEffect介紹

useEffect(didUpdate);
拆解
useEffect(callback,array)

useEffect也是componentDidMount、componentDidUpdate和componentWillUnmount這⼏個⽣命週期方法的統⼀,該 Hook 接收一個包含命令式、且可能有副作用代碼的函數。

  • callback: 回調函數,作⽤是處理副作⽤邏輯
  • array(可選參數):數組,⽤於控制useEffect的執⾏

在函數組件主體內(這裏指在 React 渲染階段)改變 DOM、添加訂閱、設置定時器、記錄日誌以及執行其他包含副作用的操作都是不被允許的,因爲這可能會產生莫名其妙的 bug 並破壞 UI 的一致性,useEffect就是爲了處理這些副作用⽽生的。

使用 useEffect 完成副作用操作。賦值給 useEffect 的函數會在組件渲染到屏幕之後執行。你可以把 effect 看作從 React 的純函數式世界通往命令式世界的逃生通道。

默認情況下,effect 將在每輪渲染結束後執行,但你可以選擇讓它 在只有某些值改變的時候才執行。

useEffect使用

簡單例子,有這樣一個需求,需要我們在組件在狀態更新的時候改變 document.title,如下:

 
import {useState,useEffect} from 'react'
const App=() => {
const [count,setState]=useState(0)
useEffect(() =>{ //更新⻚面標題
	document.title=`您點擊了了${count}次了了哦` },[count])
    return (
      <div>
	 	<div>你點擊了了{count}</div>
		<button onClick={()=>setState(count+1)}>點 擊</button>
	  </div> 
	)
}

重點:useEffect 會返回一個回調函數,作用於清除上一次副作用遺留下來的狀態,如果該 useEffect 只調用一次,該回調函數相當於 componentWillUnmount 生命週期。

 
useEffect(() =>{ 
	//副作⽤邏輯xxxxxx
	return ()=>{
	//清理副作用需要清理的內容 //類似於componentWillUnmount,組件渲染和組件卸載前執⾏的代碼
	}
 },[])

比如:通常,組件卸載時需要清除 effect 創建的諸如訂閱或計時器 ID 等資源。要實現這一點,useEffect 函數需返回一個清除函數。

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // 清除訂閱
    subscription.unsubscribe();
  };
});

爲防止內存泄漏,清除函數會在組件卸載前執行。另外,如果組件多次渲染(通常如此),則在執行下一個 effect 之前,上一個 effect 就已被清除。

effect 的條件執行

條件執行其實array(可選參數)數組有關,用於控制useEffect的執行。

  1. 什麼都不傳,組件每次 render 之後 useEffect 都會調用,相當於 componentDidMount 和 componentDidUpdate。
  2. 傳入一個空數組 [], 只會調用一次,相當於 componentDidMount 和 componentWillUnmount。
  3. 傳入一個數組,其中包括變量,只有這些變量變動時,useEffect 纔會執行。

具體看下面例子

  function App () {
    const [ count, setCount ] = useState(0)
    const [ width, setWidth ] = useState(document.body.clientWidth)

    const onChange = () => {
      
      setWidth(document.body.clientWidth)
    }

    useEffect(() => {
      window.addEventListener('resize', onChange, false)

      return () => {
        window.removeEventListener('resize', onChange, false)
      }
    })

    useEffect(() => {
      document.title = count
    })

    return (
      <div>
        名稱: { count } 
        寬度: { width }
        <button onClick={() => { setCount(count + 1)}}>點我</button>
      </div>
      )
  }

這例子要處理兩種副作用邏輯,這裏我們既要處理 title,還要監聽屏幕寬度改變,按照 class 的寫法,我們要在生命週期中處理這兩種邏輯,但在 hooks 中,我們只需要兩個 useEffect 就能解決這些問題,我們之前提到,useEffect 能夠返回一個函數,用來清除上一次副作用留下的狀態,這個地方我們可以用來解綁事件監聽,這個地方存在一個問題,就是 useEffect 是每次 render 之後就會調用,比如 title 的改變,相當於 componentDidUpdate,但我們的事件監聽不應該每次 render 之後,進行一次綁定和解綁,就是我們需要 useEffect 變成 componentDidMount, 它的返回函數變成 componentWillUnmount,這裏就需要用到 useEffect 函數的第二個參數了。

改寫:

  function App () {
    const [ count, setCount ] = useState(0)
    const [ width, setWidth ] = useState(document.body.clientWidth)

    const onChange = () => {
      setWidth(document.body.clientWidth)
    }

    useEffect(() => {
      // 相當於 componentDidMount
      console.log('add resize event')
      window.addEventListener('resize', onChange, false)

      return () => {
        // 相當於 componentWillUnmount
        window.removeEventListener('resize', onChange, false)
      }
    }, [])

    useEffect(() => {
      // 相當於 componentDidUpdate
      document.title = count
    })

    useEffect(() => {
      console.log(`count change: count is ${count}`)
    }, [ count ])

    return (
      <div>
        名稱: { count } 
        寬度: { width }
        <button onClick={() => { setCount(count + 1)}}>點我</button>
      </div>
      )
  }

第一個 useEffect 中的 ‘add resize event’ 只會在第一次運行時輸出一次,無論組件怎麼 render,都不會在輸出,第二個 useEffect 會在每次組件 render 之後都執行,title 每次點擊都會改變, 第三個 useEffect, 只要有在第一次運行和 count 改變時,纔會執行,屏幕發生改變引起的 render 並不會影響第三個 useEffect。

官方推薦啓用 eslint-plugin-react-hooks 中的 exhaustive-deps
規則。此規則會在添加錯誤依賴時發出警告並給出修復建議。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章