摘要:還可以返回另一個回調(diào)函數(shù),這個函數(shù)的執(zhí)行時機很重要。對于第二個我們可以通過返回一個回調(diào)函數(shù)來注銷事件的注冊。回調(diào)函數(shù)在視圖被銷毀之前觸發(fā),銷毀的原因有兩種重新渲染和組件卸載。
本文是 React 系列的第二篇
React 新特性講解及實例(一)
想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!
什么是 HooksHook 是 React 16.8 的新增特性。它可以讓你在不編寫 類組件 的情況下使用 state 以及其他的 React 特性。
類組件的不足狀態(tài)邏輯復(fù)用難
缺少復(fù)用機制
渲染屬性和高階組件導(dǎo)致層級冗余
趨向復(fù)雜難以維護(hù)
生命周期函數(shù)混雜不相干邏輯
相干邏輯分散在不同生命周期
this 指向困擾
內(nèi)聯(lián)函數(shù)過度創(chuàng)建新句柄
類成員函數(shù)不能保證 this
Hooks 優(yōu)勢優(yōu)化類組件的三大問題
函數(shù)組件無 this 問題
自定義 Hook 方便復(fù)用狀態(tài)邏輯
副作用的關(guān)注點分離
使用 State Hookimport React, {Component} from "react" class App extends Component { state = { count: 0 }; render() { const {count} = this.state; return ( ) } } export default App;
以上代碼很好理解,點擊按鈕讓 count 值加 1。
接下來我們使用 useState 來實現(xiàn)上述功能。
import React, {useState} from "react" function App () { const [count, setCount] = useState(0) return ( ) }
在這里,useState 就是一個 Hook。通過在函數(shù)組件里調(diào)用它來給組件添加一些內(nèi)部 state,React 會在重復(fù)渲染時保留這個 state。
useState 會返回一對值:當(dāng)前狀態(tài)和一個讓你更新它的函數(shù)。你可以在事件處理函數(shù)中或其他一些地方調(diào)用這個函數(shù)。它類似 class 組件的 this.setState,但是它不會把新的 state 和舊的 state 進(jìn)行合并。useState 唯一的參數(shù)就是初始 state。
useState 讓代碼看起來簡潔了,但是我們可能會對組件中,直接調(diào)用 useState 返回的狀態(tài)會有些懵。既然 userState 沒有傳入任何的環(huán)境參數(shù),它怎么知道要返回的的是 count 的呢,而且還是這個組件的 count 不是其它組件的 count。
初淺的理解: useState 確實不知道我們要返回的 count,但其實也不需要知道,它只要返回一個變量就行了。數(shù)組解構(gòu)的語法讓我們在調(diào)用 useState 時可以給 state 變量取不同的名字。
useState 怎么知道要返回當(dāng)前組件的 state?
因為 JavaScript 是單線程的。在 useState 被調(diào)用時,它只能在唯一一個組件的上下文中。
有人可能會問,如果組件內(nèi)有多個 usreState,那 useState 怎么知道哪一次調(diào)用返回哪一個 state 呢?
這個就是按照第一次運行的次序來順序來返回的。
接著上面的例子我們在聲明一個 useState:
... const [count, setScount] = useState(0) const [name, setName] = useState("小智") ...
然后我們就可以斷定,以后APP組件每次渲染的時候,useState 第一次調(diào)用一定是返回 count,第二次調(diào)用一定是返回 name。不信的話來做個實驗:
let id = 0 function App () { let name,setName; let count,setCount; id += 1; if (id & 1) { // 奇數(shù) [count, setCount] = useState(0) [name, setName] = useState("小智") } else { // 偶數(shù) [name, setName] = useState("小智") [count, setCount] = useState(0) } return ( ) }
首先在外部聲明一個 id,當(dāng) id為奇數(shù)和偶數(shù)的時候分別讓 useState 調(diào)用方式相反,運行會看到有趣的現(xiàn)象。
當(dāng)前版本如果寫的順序不一致就會報錯。
會發(fā)現(xiàn) count 和 name 的取值串了。我們希望給 count 加 1,現(xiàn)在卻給 name 加了 1,說明 setCount 函數(shù)也串成了 setName 函數(shù)。
為了防止我們使用 useState 不當(dāng),React 提供了一個 ESlint 插件幫助我們檢查。
eslint-plugin-react-hooks
優(yōu)化點通過上述我們知道 useState 有個默認(rèn)值,因為是默認(rèn)值,所以在不同的渲染周期去傳入不同的值是沒有意義的,只有第一次傳入的才有效。如下所示:
... const defaultCount = props.defaultCount || 0 const [count, setCount] = useState(defaultCount) ...
state 的默認(rèn)值是基于 props,在 APP 組件每次渲染的時候 const defaultCount = props.defaultCount || 0 都會運行一次,如果它復(fù)雜度比較高的話,那么浪費的資料肯定是可觀的。
useState 支持傳入函數(shù),來延遲初始化:
const [count, setCount] = useState(() => { return props.defaultCount || 0 })使用 Effect Hook
Effect Hook 可以讓你在函數(shù)組件中執(zhí)行副作用操作。數(shù)據(jù)獲取,設(shè)置訂閱以及手動更改 React 組件中的 DOM 都屬于副作用。不管你知不知道這些操作,或是"副作用"這個名字,應(yīng)該都在組件中使用過它們。
副作用的時機Mount 之后 對應(yīng) componentDidMount
Update 之后 對應(yīng) componentDidUpdate
Unmount 之前 對應(yīng) componentWillUnmount
現(xiàn)在使用 useEffect 就可以覆蓋上述的情況。
為什么一個 useEffect 就能涵蓋 Mount,Update,Unmount 等場景呢。
useEffect 標(biāo)準(zhǔn)上是在組件每次渲染之后調(diào)用,并且會根據(jù)自定義狀態(tài)來決定是否調(diào)用還是不調(diào)用。
第一次調(diào)用就相當(dāng)于componentDidMount,后面的調(diào)用相當(dāng)于 componentDidUpdate。useEffect 還可以返回另一個回調(diào)函數(shù),這個函數(shù)的執(zhí)行時機很重要。作用是清除上一次副作用遺留下來的狀態(tài)。
比如一個組件在第三次,第五次,第七次渲染后執(zhí)行了 useEffect 邏輯,那么回調(diào)函數(shù)就會在第四次,第六次和第八次渲染之前執(zhí)行。嚴(yán)格來講,是在前一次的渲染視圖清除之前。如果 useEffect 是在第一次調(diào)用的,那么它返回的回調(diào)函數(shù)就只會在組件卸載之前調(diào)用了,也就是
componentWillUnmount 。
如果你熟悉 React class 的生命周期函數(shù),你可以把 useEffect Hook 看做componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數(shù)的組合。
舉粟說明一下:
class App extends Component { state = { count: 0, size: { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } }; onResize = () => { this.setState({ size: { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } }) } componentDidMount () { document.title = this.state.count; window.addEventListener("resize", this.onResize, false) } componentWillMount () { window.removeEventListener("resize", this.onResize, false) } componentDidUpdate () { document.title = this.state.count; } render() { const {count, size} = this.state; return ( ) } }
上面主要做的就是網(wǎng)頁 title 顯示count 值,并監(jiān)聽網(wǎng)頁大小的變化。這里用到了componentDidMount,componentDidUpdate 等副作用,因為第一次掛載我們需要把初始值給 title, 當(dāng) count 變化時,把變化后的值給它 title,這樣 title 才能實時的更新。
注意,我們需要在兩個生命周期函數(shù)中編寫重復(fù)的代碼。
這邊我們?nèi)菀壮鲥e的地方就是在組件結(jié)束之后要記住銷毀事件的注冊,不然會導(dǎo)致資源的泄漏。現(xiàn)在我們把 App 組件的副作用用 useEffect 實現(xiàn)。
function App (props) { const [count, setCount] = useState(0); const [size, setSize] = useState({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }); const onResize = () => { setSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } ) } useEffect(() => { document.title = count; }) useEffect(() => { window.addEventListener("resize", onResize, false); return () => { window.removeEventListener("resize", onResize, false) } }, []) return ( ) }
對于上述代碼的第一個 useEffect,相比類組件,Hooks 不在關(guān)心是 mount 還是 update。用useEffect統(tǒng)一在渲染后調(diào)用,就完整追蹤了 count 的值。
對于第二個 useEffect,我們可以通過返回一個回調(diào)函數(shù)來注銷事件的注冊。回調(diào)函數(shù)在視圖被銷毀之前觸發(fā),銷毀的原因有兩種:重新渲染和組件卸載。
這邊有個問題,既然 useEffect 每次渲染后都執(zhí)行,那我們每次都要綁定和解綁事件嗎?當(dāng)然是完全不需要,只要使用 useEffect 第二個參數(shù),并傳入一個空數(shù)組即可。第二個參數(shù)是一個可選的數(shù)組參數(shù),只有數(shù)組的每一項都不變的情況下,useEffect 才不會執(zhí)行。第一次渲染之后,useEffect 肯定會執(zhí)行。由于我們傳入的空數(shù)組,空數(shù)組與空數(shù)組是相同的,因此 useEffect 只會在第一次執(zhí)行一次。
這也說明我們把 resize 相關(guān)的邏輯放在一直寫,不在像類組件那樣分散在兩個不同的生命周期內(nèi)。同時我們處理 title 的邏輯與 resize 的邏輯分別在兩個 useEffect 內(nèi)處理,實現(xiàn)關(guān)注點分離。
我們在定義一個 useEffect,來看看通過不同參數(shù),第二個參數(shù)的不同作用。
... useEffect(() => { console.log("count:", count) }, [count]) ...
第二個參數(shù)我們傳入 [count], 表示只有 count 的變化時,我才打印 count 值,resize 變化不會打印。
運行效果如下:
第二個參數(shù)的三種形態(tài),undefined,空數(shù)組及非空數(shù)組,我們都經(jīng)歷過了,但是咱們沒有看到過回調(diào)函數(shù)的執(zhí)行。
現(xiàn)在有一種場景就是在組件中訪問 Dom 元素,在 Dom元素上綁定事件,在上述的代碼中添加以下代碼:
... const onClick = () => { console.log("click"); } useEffect(() => { document.querySelector("#size").addEventListener("click", onClick, false); },[]) return (... size: {size.width}x{size.height})
新增一個 DOM 元素,在新的 useEffect 中監(jiān)聽 span 元素的點擊事件。
運行效果:
假如我們 span 元素可以被銷毀重建,我們看看會發(fā)生什么情況,改造一下代碼:
return (... { count%2 ? 我是span :我是p
}
運行效果:
可以看出一旦 dom 元素被替換,我們綁定的事件就失效了,所以咱們始終要追蹤這個dom 元素的最新狀態(tài)。
使用 useEffect ,最合適的方式就是使用回調(diào)函數(shù)來處理了,同時要保證每次渲染后都要重新運行,所以不能給第二次參數(shù)設(shè)置 [],改造如下:
useEffect(() => { document.querySelector("#size").addEventListener("click", onClick, false); return () => { document.querySelector("#size").removeEventListener("click", onClick, false); } })
運行結(jié)果:
參考React 官方文檔
《React勁爆新特性Hooks 重構(gòu)去哪兒網(wǎng)》
交流React 官方文檔
《React勁爆新特性Hooks 重構(gòu)去哪兒網(wǎng)》
干貨系列文章匯總?cè)缦拢X得不錯點個Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號「大遷世界」作者,對前端技術(shù)保持學(xué)習(xí)愛好者。我會經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號,后臺回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/104868.html
摘要:粟例說明一下獲取子組件或者節(jié)點的句柄指向已掛載到上的文本輸入元素本質(zhì)上,就像是可以在其屬性中保存一個可變值的盒子。粟例說明一下渲染周期之間的共享數(shù)據(jù)的存儲上述使用聲明兩個副作用,第一個每隔一秒對加,因為只需執(zhí)行一次,所以每二個參為空數(shù)組。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! React 新特性講解及實例(一) React 新特性 Hooks 講解及實...
摘要:來個使用類形式的例子以上就不說解釋了,第一篇已經(jīng)講過了,接著將改成用的形式接著使用形式接收一個對象的返回值并返回該的當(dāng)前值。如果的返回值是函數(shù)的話,那么就可以簡寫成的方式,只是簡寫而已,實際并沒有區(qū)別。 本文是 React 系列的第三篇 React 新特性講解及實例(一) React 新特性 Hooks 講解及實例(二) 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著...
摘要:接收一個屬性,這個組件會讓后代組件統(tǒng)一提供這個變量值。因此對于同一個對象而言,一定是后代元素。解決方法就是把內(nèi)聯(lián)函數(shù)提取出來,如下講了這么多,我們還沒有講到其實我們已經(jīng)講完了的工作原理了。 本節(jié)主要講解以下幾個新的特性: Context ContextType lazy Suspense 錯誤邊界(Error boundaries) memo 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博...
摘要:返回元素的是將新的與原始元素的淺層合并后的結(jié)果。生命周期方法要如何對應(yīng)到函數(shù)組件不需要構(gòu)造函數(shù)。除此之外,可以認(rèn)為的設(shè)計在某些方面更加高效避免了需要的額外開支,像是創(chuàng)建類實例和在構(gòu)造函數(shù)中綁定事件處理器的成本。 React系列 React系列 --- 簡單模擬語法(一)React系列 --- Jsx, 合成事件與Refs(二)React系列 --- virtualdom diff算法實...
摘要:第一次了解這項特性的時候,真的有一種豁然開朗,發(fā)現(xiàn)新大陸的感覺。在絕大多數(shù)情況下,是更好的選擇。唯一例外的就是需要根據(jù)新的來進(jìn)行操作的場景。會保證在頁面渲染前執(zhí)行,也就是說頁面渲染出來的是最終的效果。上面條規(guī)則都是為了保證調(diào)用順序的穩(wěn)定性。 歡迎關(guān)注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...
閱讀 2171·2020-06-12 14:26
閱讀 2477·2019-08-29 16:41
閱讀 1884·2019-08-29 15:28
閱讀 2447·2019-08-26 13:43
閱讀 753·2019-08-26 13:37
閱讀 2772·2019-08-23 18:13
閱讀 2791·2019-08-23 15:31
閱讀 1013·2019-08-23 14:10