React Hooks淺析~

Hook 是 React 16.8 的新增特性。它可以讓你在不編寫(xiě) class 的情況下使用 state 以及其他的 React 特性犬庇。

Hook 是什么键畴??Hook 是一個(gè)特殊的函數(shù)服赎,它可以讓你“鉤入” React 的特性祸轮。例如,useState?是允許你在 React 函數(shù)組件中添加 state 的 Hook哀峻。

react hooks的內(nèi)置hook有:

useState()涡相、useEffect()、useContext()剩蟀、useMemo()催蝗、useCallback()、useRef()育特、useImperativeHandle()丙号、自定義hook


useState()?

先來(lái)看一下用useState()和class組件操作state的不同吧先朦!

使用useState()

使用class操作state

在constructor()構(gòu)造函數(shù)中,通過(guò)this.state對(duì)象進(jìn)行初始化設(shè)置{count:0}把this.state.count的初始值設(shè)置為1犬缨,在函數(shù)組件中喳魏,沒(méi)有this,需要用到useState()Hook來(lái)操作state變量怀薛。

調(diào)用 useState?方法的時(shí)候做了什么??它定義一個(gè) “state 變量”--count刺彩,這是一種在函數(shù)調(diào)用時(shí)保存變量的方式 ,它與 class 里面的?this.state?提供的功能完全相同枝恋。一般來(lái)說(shuō)创倔,在函數(shù)退出后變量就會(huì)”消失”,而 state 中的變量會(huì)被 React 保留 ---- 閉包原理焚碌。

useState?需要哪些參數(shù)畦攘??useState()?方法里面唯一的參數(shù)就是初始 state,在示例中十电,只需使用數(shù)字來(lái)記錄用戶(hù)點(diǎn)擊的次數(shù)知押,所以我們傳了?0?作為變量的初始 state(如果需要在 state 中存儲(chǔ)兩個(gè)不同的變量,只需調(diào)用?useState()?兩次即可)鹃骂。

useState?方法的返回值是什么台盯??返回值為:當(dāng)前 state 以及更新 state 的函數(shù)。這就是我們寫(xiě)?const [count, setCount] = useState()?的原因偎漫。這與 class 里面this.state.count?和?this.setState?類(lèi)似爷恳,唯一區(qū)別就是你需要成對(duì)的獲取它們有缆。

讀取state變量方式的不同:

通過(guò)class定義的state:{ this.state.count }函數(shù)式組件獲取state變量:{ count }

更新state變量方式的不同:

通過(guò)class定義的state:需要調(diào)用?this.setState()?來(lái)更新?count?值

通過(guò)function定義的state:在定義的函數(shù)式組件中已經(jīng)有了?setCount?和?count?變量象踊,所以我們不需要?this,可通過(guò)setCount直接操作state棚壁。

函數(shù)式組件代碼解讀:

第一行:?引入 React 中的?useState?Hook杯矩。它讓我們?cè)诤瘮?shù)組件中存儲(chǔ)內(nèi)部 state。

第四行:?在 Test 組件內(nèi)部袖外,我們通過(guò)調(diào)用?useState?Hook 聲明了一個(gè)新的 state 變量史隆。它返回一對(duì)值給到我們命名的變量上。我們把變量命名為?count曼验,因?yàn)樗鎯?chǔ)的是點(diǎn)擊次數(shù)泌射。我們通過(guò)傳?0?作為?useState?唯一的參數(shù)來(lái)將其初始化為?0。第二個(gè)返回的值本身就是一個(gè)函數(shù)鬓照。它讓我們可以更新?count?的值熔酷,所以我們叫它?setCount。

第八行:?當(dāng)用戶(hù)點(diǎn)擊按鈕后豺裆,我們傳遞一個(gè)新的值給?setCount拒秘。React 會(huì)重新渲染 Test 組件,并把最新的?count?傳給它。

"[ ]"是數(shù)組解構(gòu)的操作

const[fruit,setFruit]=useState('banana'); 等價(jià)于下邊代碼? ? ? ? ? ? ? varfruitStateVariable=useState('banana');// 返回一個(gè)有兩個(gè)元素的數(shù)組varfruit=fruitStateVariable[0];// 數(shù)組里的第一個(gè)值? ? ? ? ? ? ? ? ? ? ? ? ? varsetFruit=fruitStateVariable[1];// 數(shù)組里的第二個(gè)值

useEffect()

在useEffect hook可以在函數(shù)里執(zhí)行一些副作用操作躺酒,如:數(shù)據(jù)獲取的請(qǐng)求押蚤,設(shè)置訂閱(事件監(jiān)聽(tīng))、手動(dòng)更改 React 組件中的 DOM 都屬于useEffect()(副作用)羹应。

我理解的副作用就是在不停的完善通過(guò)腳手架搭建出來(lái)的一個(gè)空架子揽碘,從形式上看,useEffect()=componentDidMount+componentDidUpdate+componentWillUnmount量愧。useEffect中的方法钾菊,是在render()完DOM后再執(zhí)行的,好比于class組件中執(zhí)行完render后再執(zhí)行componentDidMount方法一樣偎肃。

在 React 組件中有兩種常見(jiàn)副作用操作:需要清除的和不需要清除的

無(wú)需清除的 effect:

有時(shí)候煞烫,我們只想在 React 更新 DOM 之后運(yùn)行一些額外的代碼。比如發(fā)送網(wǎng)絡(luò)請(qǐng)求累颂,手動(dòng)變更 DOM滞详,記錄日志,這些都是常見(jiàn)的無(wú)需清除的操作紊馏。

class組件中的副作用操作:

在 React 的 class 組件中料饥,render?函數(shù)是沒(méi)有任何副作用的。這就是為什么在 React class 中朱监,我們把副作用操作放到?componentDidMount?和?componentDidUpdate?函數(shù)中岸啡。如下:

我們可以看到,在這個(gè) class 中赫编,我們需要在兩個(gè)生命周期函數(shù)中編寫(xiě)重復(fù)的代碼巡蘸。

使用function組件實(shí)現(xiàn)dom的加載和更新:

默認(rèn)情況下,useEffect在第一次渲染之后每次更新之后都會(huì)執(zhí)行擂送。稍后進(jìn)行配置后可以實(shí)現(xiàn)非默認(rèn)操作悦荒。同時(shí)React 保證了每次運(yùn)行 effect 的同時(shí),DOM 都已經(jīng)更新完畢嘹吨。

性能方面:

與?componentDidMount?或?componentDidUpdate?不同搬味,使用?useEffect?調(diào)度的 effect 不會(huì)阻塞瀏覽器更新屏幕,這讓?xiě)?yīng)用看起來(lái)響應(yīng)更快蟀拷。大多數(shù)情況下碰纬,effect 不需要同步地執(zhí)行。在個(gè)別情況下(例如測(cè)量布局)问芬,有單獨(dú)的?useLayoutEffect?Hook 使用悦析,其 API 與?useEffect?相同。

需要清除的 effect副作用:

像訂閱外部數(shù)據(jù)源愈诚,清除工作是非常重要的她按,可以防止引起內(nèi)存泄露牛隅!

使用 Class 的示例:在 React class 中,通常會(huì)在?componentDidMount?中設(shè)置訂閱酌泰,并在?componentWillUnmount?中清除它媒佣。


在 React class 中,你通常會(huì)在?componentDidMount?中設(shè)置訂閱陵刹,并在?componentWillUnmount?中清除它默伍。

使用 Hook 的示例

由于添加和刪除訂閱的代碼的緊密性,所以?useEffect?的設(shè)計(jì)是在同一個(gè)地方執(zhí)行衰琐。如果你的 effect 返回一個(gè)函數(shù)也糊,React 將會(huì)在執(zhí)行清除操作時(shí)調(diào)用它:

return 的函數(shù)也可以是箭頭函數(shù)

為什么要在 effect 中返回一個(gè)函數(shù)??這是 effect 可選的清除機(jī)制羡宙。每個(gè) effect 都可以返回一個(gè)清除函數(shù)狸剃。如此可以將添加和移除訂閱的邏輯放在一起。它們都屬于 effect 的一部分狗热。

React 何時(shí)清除 effect钞馁??React 會(huì)在組件卸載的時(shí)候執(zhí)行清除操作。正如之前學(xué)到的匿刮,effect 在每次渲染的時(shí)候都會(huì)執(zhí)行僧凰。這就是為什么 React?會(huì)在執(zhí)行當(dāng)前 effect 之前對(duì)上一個(gè) effect 進(jìn)行清除。

使用 Hook 其中一個(gè)目的就是要解決 class 中生命周期函數(shù)經(jīng)常包含不相關(guān)的邏輯熟丸,但又把相關(guān)邏輯分離到了幾個(gè)不同方法中的問(wèn)題训措。

可以發(fā)現(xiàn) document.title?的邏輯被分割到?componentDidMount?和?componentDidUpdate?中,訂閱邏輯又被分割到?componentDidMount?和?componentWillUnmount?中光羞。而且?componentDidMount?中同時(shí)包含了兩個(gè)不同功能的代碼绩鸣。當(dāng)功能需求i更多時(shí),邏輯處理非常容易混亂狞山。

那么 Hook 如何解決這個(gè)問(wèn)題呢全闷?可以使用多個(gè) effect叉寂,會(huì)將不相關(guān)邏輯分離到不同的 effect 中:

Hook 允許我們按照代碼的用途分離他們 萍启, React 將按照 effect 聲明的順序依次調(diào)用組件中的每一個(gè)?effect。

并不是必須為 effect 中返回的函數(shù)命名屏鳍。這里我們將其命名為?cleanup?是為了表明此函數(shù)的目的勘纯,但其實(shí)也可以返回一個(gè)箭頭函數(shù)或者給起一個(gè)別的名字。

class組件:

從 class 中 props 讀取?friend.id钓瞭,然后在組件掛載后componentDidMount()訂閱好友的狀態(tài)驳遵,并在卸載組件componentWillUnmount()的時(shí)候取消訂閱。但是當(dāng)組件已經(jīng)顯示在屏幕上時(shí)山涡,state變化時(shí)會(huì)發(fā)生什么堤结?-- 我們的組件將繼續(xù)展示原來(lái)的state唆迁。而且我們還會(huì)因?yàn)槿∠嗛啎r(shí)使用錯(cuò)誤的 ID 導(dǎo)致內(nèi)存泄露或崩潰的問(wèn)題,所以在 class 組件中竞穷,我們需要添加?componentDidUpdate?來(lái)解決這個(gè)問(wèn)題:

忘記正確地處理?componentDidUpdate?是 React 應(yīng)用中常見(jiàn)的 bug 來(lái)源

https://react.docschina.org/docs/hooks-effect.html?--?useEffect hook官網(wǎng)鏈接

并不是每一次的dom渲染都必須執(zhí)行useEffect()的唐责,畢竟渲染是很消耗性能能的,所以在當(dāng)dom加載的數(shù)據(jù)與上次渲染的數(shù)據(jù)一致時(shí)瘾带,應(yīng)當(dāng)跳過(guò)useEffect()操作鼠哥,優(yōu)化性能,那么該如何實(shí)現(xiàn)這樣的操作呢看政?

在 class 組件中朴恳,我們可以通過(guò)在?componentDidUpdate?中添加對(duì)?prevProps?或?prevState?的比較邏輯解決:

如果某些特定值在兩次重渲染之間沒(méi)有發(fā)生變化,你可以通知 React?跳過(guò)對(duì) effect 的調(diào)用允蚣,只要傳遞數(shù)組作為?useEffect?的第二個(gè)可選參數(shù)即可:


而useEffect?默認(rèn)就會(huì)處理更新問(wèn)題于颖,對(duì)前一個(gè)useEffect() 進(jìn)行清理。

同理嚷兔,對(duì)于有清除操作的 effect 同樣適用

1恍飘、僅執(zhí)行一次。給useEffect多傳一個(gè)空數(shù)組[]谴垫,比如:

useEffect(()=>{document.title=`Clicked ${count} times`;},[]);

2章母、選擇性的執(zhí)行。給useEffect多傳一個(gè)[count]翩剪,比如:

useEffect(()=>{document.title=`Clicked ${count} times`;},[count]);

數(shù)組參數(shù)前面的方法乳怎,是否執(zhí)行,依賴(lài)于數(shù)組的值前后兩次是否變化前弯。

3蚪缀、每次刷新都執(zhí)行一遍。不傳任何參數(shù)恕出,比如:

useEffect(()=>{document.title=`Clicked ${count} times`;});

useContext

看下方代碼便可大致了解useContext()的用法啦~

借助React.createContext 和 useContext()询枚,我們擁有了一種 “透?jìng)鳌?的的能力,能將頂層的屬性浙巫,一次傳遞到任意子層級(jí)的組件金蜀,而不需要層層接力式的傳遞。

useReducer:

useState?的替代方案的畴。它接收一個(gè)形如?(state, action) => newState?的 reducer渊抄,并返回當(dāng)前的 state 以及與其配套的?dispatch?方法。


useCallback:

當(dāng)你把回調(diào)函數(shù)傳遞給經(jīng)過(guò)優(yōu)化的并使用引用相等性去避免非必要渲染(例如?shouldComponentUpdate)的子組件時(shí)丧裁,它將非常有用

useCallback(fn, deps)?相當(dāng)于?useMemo(() => fn, deps)

useMemo:

useMemo能記憶一個(gè)方法執(zhí)行的結(jié)果值护桦,假如下次刷新組件時(shí),依賴(lài)不變煎娇,則useMemo不會(huì)執(zhí)行這個(gè)方法二庵,而是直接拿到上次記憶的值 -- 類(lèi)似于vue中的computed贪染。如果這個(gè)方法是個(gè)耗時(shí)運(yùn)算,或是返回一個(gè)組件催享,當(dāng)依賴(lài)不變抑进,就直接拿記憶值,這樣就能起到性能優(yōu)化的效果睡陪。如果沒(méi)有提供依賴(lài)項(xiàng)數(shù)組寺渗,useMemo?在每次渲染時(shí)都會(huì)計(jì)算新的值。

useRef:

是用對(duì)象引用方式兰迫,用戶(hù)代碼可以用它來(lái)做一般數(shù)據(jù)的緩存信殊。說(shuō)白了還是一種持久化。

seRef?返回一個(gè)可變的 ref 對(duì)象汁果,其?.current?屬性被初始化為傳入的參數(shù)(initialValue)涡拘。返回的 ref 對(duì)象在組件的整個(gè)生命周期內(nèi)保持不變。

React.memo:

functionMyComponent(props){// render using props}functionareEqual(prevProps,nextProps){// return true if passing nextProps to render would return// the same result as passing prevProps to render,// otherwise return false}exportdefaultReact.memo(MyComponent,areEqual);

總結(jié)一下

1据德、useState和useRef鉤子行為相似鳄乏。

2、useContext具有透?jìng)髂芰?/p>

3棘利、其他鉤子在于依賴(lài)橱野。

4、捕獲值的這個(gè)特性是我們寫(xiě)鉤子最最需要注意的問(wèn)題善玫,它是函數(shù)特有的一種特性水援,并非函數(shù)式組件專(zhuān)有。函數(shù)的每一次調(diào)用茅郎,會(huì)產(chǎn)生一個(gè)屬于那一次調(diào)用的作用域蜗元,不同的作用域之間不受影響。

其他

react-redux的鉤子

狀態(tài)管理方面系冗,React 社區(qū)最有名的工具當(dāng)然是 Redux奕扣。在 react-redux@7.1 中新引用了三個(gè) API:

useSelector。它有點(diǎn)像 connect() 函數(shù)的第一個(gè)參數(shù) mapStateToProps掌敬,把數(shù)據(jù)從 state 中取出來(lái)惯豆;

useStore 。返回 store 本身涝开;

useDispatch循帐。返回 store.dispatch框仔。

關(guān)于測(cè)試

覺(jué)得還是到改了一部分 hooks 寫(xiě)法后舀武,在加單元測(cè)試。現(xiàn)在堆積了很多邏輯的class組件真心難寫(xiě)离斩。如何測(cè)試使用了 Hook 的組件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末银舱,一起剝皮案震驚了整個(gè)濱河市瘪匿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寻馏,老刑警劉巖棋弥,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異诚欠,居然都是意外死亡顽染,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)轰绵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)粉寞,“玉大人,你說(shuō)我怎么就攤上這事左腔∵罂眩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵液样,是天一觀(guān)的道長(zhǎng)振亮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鞭莽,這世上最難降的妖魔是什么坊秸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮澎怒,結(jié)果婚禮上妇斤,老公的妹妹穿的比我還像新娘。我一直安慰自己丹拯,他們只是感情好站超,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著乖酬,像睡著了一般死相。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咬像,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天算撮,我揣著相機(jī)與錄音,去河邊找鬼县昂。 笑死肮柜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的倒彰。 我是一名探鬼主播审洞,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼待讳!你這毒婦竟也來(lái)了芒澜?” 一聲冷哼從身側(cè)響起仰剿,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痴晦,沒(méi)想到半個(gè)月后南吮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡誊酌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年部凑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碧浊。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砚尽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辉词,到底是詐尸還是另有隱情必孤,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布瑞躺,位于F島的核電站敷搪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏幢哨。R本人自食惡果不足惜赡勘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捞镰。 院中可真熱鬧闸与,春花似錦、人聲如沸岸售。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凸丸。三九已至拷邢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屎慢,已是汗流浹背瞭稼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腻惠,地道東北人环肘。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像集灌,于是被迫代替她去往敵國(guó)和親悔雹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359