"小和山的菜鳥們",爲前端開發者提供技術相關資訊以及系列基礎文章。爲更好的用戶體驗,請您移至咱們官網小和山的菜鳥們 ( xhs-rookies.com/ ) 進行學習,及時獲取最新文章。前端
"Code tailor" ,若是您對咱們文章感興趣、或是想提一些建議,微信關注 「小和山的菜鳥們」 公衆號,與咱們取的聯繫,您也能夠在微信上觀看咱們的文章。每個建議或是贊同都是對咱們極大的鼓勵!react
這篇文章,咱們主要目的是瞭解一下 共享狀態鉤子(useEffect) 的使用.web
Effect Hook
可讓你在函數組件中執行反作用操做,換句話說能夠完成一些相似 Class 中生命週期的功能。數組
useEffect()
的使用以下面語句。微信
useEffect(() => {
dosomeing(); // 執行一些反作用操做
},[]);
複製代碼
useEffect 須要參數markdown
第一個參數:該 Hook
接收一個包含命令式、且可能有反作用代碼的函數。函數
第二個參數:該 useEffect
在哪些 state
發生變化時,才從新執行;oop
useEffect 作了什麼學習
經過 useEffect
的 Hook
,能夠告訴 React
須要在渲染後執行某些操做;而 useEffect
的第一個參數要求咱們傳遞一個函數,在 React
執行完更新 DOM
操做以後,就會執行這個函數,默認狀況下,不管是組件第一次渲染以後,仍是組件更新以後,都會執行這個函數,咱們能夠在這個要傳遞的函數中執行一些相似 Class
組件中生命週期要作的工做,好比:改變 DOM、添加訂閱、設置定時器、記錄日誌等。 要知道,在沒有這個 useEffect Hook
以前在函數組件內的這些反作用操做是不被容許的。優化
提示
若是你熟悉
React class
的生命週期函數,你能夠把useEffect
Hook 看作componentDidMount
,componentDidUpdate
和componentWillUnmount
這三個函數的組合。
在 Class
組件中,某些反作用代碼,咱們須要在 componentWillUnmount
中清除,好比咱們的建立計時器與清除計時器,一樣的,在函數組件卸載時也須要清除 effect
建立的諸如訂閱或計時器 ID 等資源。要實現這一點,useEffect
函數需返回一個清除函數。如下就是一個建立計時器的例子:
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
// 建立計時器
addCount((val) => val + 1)
}, timer)
return () => {
// 返回清除函數
clearInterval(interval) // 清除計時器
}
})
複製代碼
爲防止內存泄漏,清除函數會在組件卸載前執行。另外,若是組件屢次渲染(一般如此),則在執行下一個 effect
以前,上一個 effect
就已被清除。在上述示例中,意味着組件的每一次更新都會建立新的計時器。若想避免每次更新都觸發 effect
的執行,這就須要用到 useEffect
的第二個參數。
默認狀況下,effect
會在每輪組件渲染完成後執行。這樣的話,一旦 effect
的依賴發生變化,它就會被從新建立。
然而,在某些場景下這麼作可能會矯枉過正。好比,上面計時器的例子中,咱們並不須要在每次組件更新時都建立新的計時器,而是僅須要在 source prop
改變時從新建立。
這裏就要用到 useEffect
接受的第二個參數,它是 effect
所依賴的值數組。更新後的示例以下:
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
// 建立定時器
addCount((val) => val + 1)
}, timer)
return () => {
clearInterval(interval) // 清除定時器
}
}, [count])
複製代碼
當傳入第二參數的時候,在這裏也就是當 count
改變後纔會從新建立計時器,也就是說,useEffect
會根據咱們傳入的第二參數,來決定本次更新是否執行。
注意
- 若是你要使用此優化方式,請確保數組中包含了全部外部做用域中會發生變化且在 effect 中使用的變量,不然你的代碼會引用到先前渲染中的舊變量。
- 若是想執行只運行一次的 effect(僅在組件掛載和卸載時執行),能夠傳遞一個空數組(
[]
)做爲第二個參數。
相信你如今已經對 useEffect
有所瞭解了,那咱們用一個簡單的例子,來感覺一下在函數組件中使用 Hook
和 Class
組件的區別。
有一個簡單的計時器,默認以 1000ms
的時間間隔計數,點擊加按鈕後,計時時間間隔增長,計時器計數變慢。
首先咱們要在類組件中聲明一個變量 count
來計數,一個變量 timer
來控制計數時間間隔,每隔 timer
個時間, count
加一,而後添加一個按鈕,每次點擊時, timer
增長 1000ms
。同時 count
增長速度變慢。
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
timer: 1000,
}
}
componentDidMount() {
this.interval = setInterval(() => {
// 建立定時器
this.setState({
count: this.state.count + 1,
})
}, this.state.timer)
}
componentDidUpdate() {
clearInterval(this.interval) // 先清除上一個定時器
this.interval = setInterval(() => {
// 建立以 更新後的timer 計時間隔定時器
this.setState({
count: this.state.count + 1,
})
}, this.state.timer)
}
componentWillUnmount() {
clearInterval(this.interval) // 卸載時清除定時器
}
render() {
return (
<div> <p>當前count值爲:{this.state.count}</p> <p>當前計數時間間隔爲:{this.state.timer}ms</p> <button onClick={() => this.setState({ timer: this.state.timer + 1000 })}>Click me</button> </div>
)
}
}
複製代碼
咱們能夠發現,在這個 class 中,咱們須要在兩個生命週期函數中編寫重複的代碼。還要在組件卸載時清除計時器,大量的重複代碼,顯得很是繁瑣,那麼在函數組件中使用 Effect Hook
會是怎樣的呢?
import React, { useState, useEffect } from 'react'
function Example() {
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
addCount((val) => val + 1)
}, timer)
return () => {
clearInterval(interval)
}
}, [count])
return (
<div> <p>當前count的值爲:{count}</p> <p>當前計數時間間隔爲: {timer}ms</p> <button onClick={() => addTimer((val) => val + 1000)}>Click me</button> </div>
)
}
複製代碼
看了這個例子以後,你是否是很驚奇的發現,對比 Class
組件的 constructor
函數沒有了,寫在三個生命週期中的函數在函數組件被 useEffect
一個傢伙解決了!是否是看起來很精簡。相信你有下面幾個問題:
useEffect
作了什麼?
經過使用這個 Hook
,你能夠告訴 React
組件須要在渲染後執行某些操做。React
會保存你傳遞的函數(咱們將它稱之爲 「effect」),而且在執行 DOM
更新以後調用它。
爲何在組件內部調用 useEffect
?
將 useEffect
放在組件內部讓咱們能夠在 effect
中直接訪問 count state
變量(或其餘 props
)。
useEffect
會在每次渲染後都執行嗎?
是的,默認狀況下,它在第一次渲染以後和每次更新以後都會執行(你能夠利用前面提到的第二個參數來控制渲染條件)。並且,React
保證了每次運行 effect
的同時,DOM
都已經更新完畢。
useEffect Hook
實際上爲咱們帶來了,在函數組件中也能處理反作用的功能,他比如componentDidMount
,componentDidUpdate
和 componentWillUnmount
這三個函數的組合。
useEffect Hook
與 Class
函數的生命週期不一樣的一點:useEffect
在 DOM 都已經更新完畢才執行,componentDidMount
則是在組件掛在後(插入 DOM 樹中)當即調用,componentDidUpdate()
會在更新後會被當即調用。首次渲染不會執行此方法。componentWillUnmount()
會在組件卸載及銷燬以前直接調用。
useEffect Hook
的出現以前,組件開發中逐漸會被狀態邏輯和反作用充斥。每一個生命週期經常包含一些不相關的邏輯。而它的出現幫助咱們更好的拆分這些狀態邏輯,並不是強制按照生命週期劃分,爲開發增添了許多的可能。
在下節中,咱們將爲你們介紹 useRefs
,敬請期待!