使用Effect hook可以在函數(shù)組件中執(zhí)行副作用
副作用 - 指一段和當(dāng)前執(zhí)行結(jié)果無關(guān)的代碼。
- data fetching
- setting up a subscription
- manually changing the DOM
- logging
useEffect 可以看作是 componentDidMount, componentDidUpdate 和 componentWillUnmount 的結(jié)合,且useEffect不會(huì)阻塞瀏覽器更新screen。
useEffect 使用方法及執(zhí)行時(shí)機(jī):
-
Effects Without Cleanup - 該例子會(huì)在每次
render之后都執(zhí)行effect- 組建掛載后-
componentDidMount - 每次
update后-componentDidUpdate
- 組建掛載后-
useEffect(() => {
document.title = `You clicked ${count} times`;
})
-
Effects With Cleanup - 該例子的
effect返回了一個(gè)函數(shù)- 其他內(nèi)容在每次
render之后會(huì)執(zhí)行 - 返回的函數(shù)內(nèi)容會(huì)在組件卸載之前(等同于
componentWillUnmount)執(zhí)行
- 其他內(nèi)容在每次
useEffect(() => {
const handStatusUpdated = (status) => {
setIsOnline(status.isOnline)
}
ChatAPI.subscribe(friend.id, handStatusUpdated);
return ChatAPI.unsubscribe(friend.id, handStatusUpdated);
})
- 在同一個(gè)組件中多次使用
useEffect- React會(huì)按照指定的順序應(yīng)用每個(gè)effect -
Skipping Effects - 只有
count改變才會(huì)re-run該effect
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count])
useEffect(() => {
const handStatusUpdated = (status) => {
setIsOnline(status.isOnline)
}
ChatAPI.subscribe(friend.id, handStatusUpdated);
return ChatAPI.unsubscribe(friend.id, handStatusUpdated);
}, [friend.id])
- 只在
mount和unmount時(shí)運(yùn)行的effect- 依賴項(xiàng)傳入空數(shù)組并返回函數(shù)
useEffect(() => {
// code block mount時(shí)執(zhí)行
return () => {
// code block unmount時(shí)執(zhí)行
}
}, [])
Hooks 的依賴項(xiàng)
Hooks 提供了讓你監(jiān)聽某個(gè)數(shù)據(jù)變化的能力。這個(gè)變化可能會(huì)觸發(fā)組件的刷新,也可能是去創(chuàng)建一個(gè)副作用,又或者是刷新一個(gè)緩存。那么定義要監(jiān)聽哪些數(shù)據(jù)變化的機(jī)制,其實(shí)就是指定 Hooks 的依賴項(xiàng)。
定義依賴項(xiàng)時(shí),需要注意以下三點(diǎn):
- 依賴項(xiàng)中定義的變量一定是會(huì)在回調(diào)函數(shù)中用到的,否則聲明依賴項(xiàng)其實(shí)是沒有意義的。
- 依賴項(xiàng)一般是一個(gè)常量數(shù)組,而不是一個(gè)變量。因?yàn)橐话阍趧?chuàng)建
callback的時(shí)候,你其實(shí)非常清楚其中要用到哪些依賴項(xiàng)了。 -
React會(huì)使用淺比較來對(duì)比依賴項(xiàng)是否發(fā)生了變化,所以要特別注意數(shù)組或者對(duì)象類型。
function Sample() {
// 這里在每次組件執(zhí)行時(shí)創(chuàng)建了一個(gè)新數(shù)組
const todos = [{ text: 'Learn hooks.'}];
useEffect(() => {
console.log('Todos changed.');
}, [todos]);
}
代碼的原意可能是在 todos 變化的時(shí)候去產(chǎn)生一些副作用,但是這里的 todos 變量是在函數(shù)內(nèi)創(chuàng)建的,實(shí)際上每次都產(chǎn)生了一個(gè)新數(shù)組。
useEffect 與三種生命周期方法
useEffect(() => {
// componentDidMount + componentDidUpdate
console.log('這里基本等價(jià)于 componentDidMount + componentDidUpdate');
return () => {
// componentWillUnmount
console.log('這里基本等價(jià)于 componentWillUnmount');
}
}, [deps])
基本等價(jià)于的含義:
- 一方面,
useEffect(callback) 這個(gè)Hook接收的callback,只有在依賴項(xiàng)變化時(shí)才被執(zhí)行。而傳統(tǒng)的componentDidUpdate則一定會(huì)執(zhí)行。在componentDidUpdate中,我們通常都需要手動(dòng)判斷某個(gè)狀態(tài)是否發(fā)生變化,然后再執(zhí)行特定的邏輯。 - 另一方面,
callback返回的函數(shù)(一般用于清理工作)在下一次依賴項(xiàng)發(fā)生變化以及組件銷毀之前執(zhí)行,而傳統(tǒng)的componentWillUnmount只在組件銷毀時(shí)才會(huì)執(zhí)行。
useEffect 接收的返回值是一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)不只是會(huì)在組件銷毀時(shí)執(zhí)行,而且是每次 Effect 重新執(zhí)行之前都會(huì)執(zhí)行,用于清理上一次 Effect 的執(zhí)行結(jié)果。
總結(jié)
學(xué)習(xí)useEffect要理解什么是副作用以及使用useEffect傳入不同的參數(shù)時(shí)能達(dá)到什么效果。雖然 Hooks 在功能上基本可以映射到傳統(tǒng)的 Class 組件的生命周期方法,但是它們卻又不是完全等價(jià)的。在實(shí)現(xiàn)具體的業(yè)務(wù)功能的時(shí)候,都應(yīng)該盡量從 Hooks 的語義角度出發(fā)去思考組件是如何展現(xiàn)和交互的,這樣才能更加順滑地切換到函數(shù)組件的開發(fā)方式。
在函數(shù)組件中你要思考的方式永遠(yuǎn)是:當(dāng)某個(gè)狀態(tài)發(fā)生變化時(shí),我要做什么,而不再是在 Class 組件中的某個(gè)生命周期方法中我要做什么。