摘要:第一次了解這項(xiàng)特性的時(shí)候,真的有一種豁然開朗,發(fā)現(xiàn)新大陸的感覺。為了解決這一痛點(diǎn),才會(huì)有剪頭函數(shù)的綁定特性。它同時(shí)具備和三個(gè)生命周期函數(shù)的執(zhí)行時(shí)機(jī)。
歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:
React Hooks 是從 v16.8 引入的又一開創(chuàng)性的新特性。第一次了解這項(xiàng)特性的時(shí)候,真的有一種豁然開朗,發(fā)現(xiàn)新大陸的感覺。我深深的為 React 團(tuán)隊(duì)天馬行空的創(chuàng)造力和精益求精的鉆研精神所折服。本文除了介紹具體的用法外,還會(huì)分析背后的邏輯和使用時(shí)候的注意事項(xiàng),力求做到知其然也知其所以然。
這個(gè)系列分上下兩篇,這里是下篇的傳送門:
React Hooks 解析(下):進(jìn)階
Hooks的出現(xiàn)是為了解決 React 長(zhǎng)久以來存在的一些問題:
帶組件狀態(tài)的邏輯很難重用
為了解決這個(gè)問題,需要引入render props或higher-order components這樣的設(shè)計(jì)模式,如react-redux提供的connect方法。這種方案不夠直觀,而且需要改變組件的層級(jí)結(jié)構(gòu),極端情況下會(huì)有多個(gè)wrapper嵌套調(diào)用的情況。
Hooks可以在不改變組件層級(jí)關(guān)系的前提下,方便的重用帶狀態(tài)的邏輯。
復(fù)雜組件難于理解
大量的業(yè)務(wù)邏輯需要放在componentDidMount和componentDidUpdate等生命周期函數(shù)中,而且往往一個(gè)生命周期函數(shù)中會(huì)包含多個(gè)不相關(guān)的業(yè)務(wù)邏輯,如日志記錄和數(shù)據(jù)請(qǐng)求會(huì)同時(shí)放在componentDidMount中。另一方面,相關(guān)的業(yè)務(wù)邏輯也有可能會(huì)放在不同的生命周期函數(shù)中,如組件掛載的時(shí)候訂閱事件,卸載的時(shí)候取消訂閱,就需要同時(shí)在componentDidMount和componentWillUnmount中寫相關(guān)邏輯。
Hooks可以封裝相關(guān)聯(lián)的業(yè)務(wù)邏輯,讓代碼結(jié)構(gòu)更加清晰。
難于理解的 Class 組件
JS 中的this關(guān)鍵字讓不少人吃過苦頭,它的取值與其它面向?qū)ο笳Z言都不一樣,是在運(yùn)行時(shí)決定的。為了解決這一痛點(diǎn),才會(huì)有剪頭函數(shù)的this綁定特性。另外 React 中還有Class Component和Function Component的概念,什么時(shí)候應(yīng)該用什么組件也是一件糾結(jié)的事情。代碼優(yōu)化方面,對(duì)Class Component進(jìn)行預(yù)編譯和壓縮會(huì)比普通函數(shù)困難得多,而且還容易出問題。
Hooks可以在不引入 Class 的前提下,使用 React 的各種特性。
三、什么是 HooksHooks are functions that let you “hook into” React state and lifecycle features from function components
上面是官方解釋。從中可以看出 Hooks 是函數(shù),有多個(gè)種類,每個(gè) Hook 都為Function Component提供使用 React 狀態(tài)和生命周期特性的通道。Hooks 不能在Class Component中使用。
React 提供了一些預(yù)定義好的 Hooks 供我們使用,下面我們來詳細(xì)了解一下。
四、State Hook先來看一個(gè)傳統(tǒng)的Class Component:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return (); } }You clicked {this.state.count} times
使用 State Hook 來改寫會(huì)是這個(gè)樣子:
import React, { useState } from "react"; function Example() { // 定義一個(gè) State 變量,變量值可以通過 setCount 來改變 const [count, setCount] = useState(0); return (); }You clicked {count} times
可以看到useState的入?yún)⒅挥幸粋€(gè),就是 state 的初始值。這個(gè)初始值可以是一個(gè)數(shù)字、字符串或?qū)ο螅踔量梢允且粋€(gè)函數(shù)。當(dāng)入?yún)⑹且粋€(gè)函數(shù)的時(shí)候,這個(gè)函數(shù)只會(huì)在這個(gè)組件初始渲染的時(shí)候執(zhí)行:
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; });
useState的返回值是一個(gè)數(shù)組,數(shù)組的第一個(gè)元素是 state 當(dāng)前的值,第二個(gè)元素是改變 state 的方法。這兩個(gè)變量的命名不需要遵守什么約定,可以自由發(fā)揮。要注意的是如果 state 是一個(gè)對(duì)象,setState 的時(shí)候不會(huì)像Class Component的 setState 那樣自動(dòng)合并對(duì)象。要達(dá)到這種效果,可以這么做:
setState(prevState => { // Object.assign 也可以 return {...prevState, ...updatedValues}; });
從上面的代碼可以看出,setState 的參數(shù)除了數(shù)字、字符串或?qū)ο?,還可以是函數(shù)。當(dāng)需要根據(jù)之前的狀態(tài)來計(jì)算出當(dāng)前狀態(tài)值的時(shí)候,就需要傳入函數(shù)了,這跟Class Component的 setState 有點(diǎn)像。
另外一個(gè)跟Class Component的 setState 很像的一點(diǎn)是,當(dāng)新傳入的值跟之前的值一樣時(shí)(使用Object.is比較),不會(huì)觸發(fā)更新。
五、Effect Hook解釋這個(gè) Hook 之前先理解下什么是副作用。網(wǎng)絡(luò)請(qǐng)求、訂閱某個(gè)模塊或者 DOM 操作都是副作用的例子,Effect Hook 是專門用來處理副作用的。正常情況下,在Function Component的函數(shù)體中,是不建議寫副作用代碼的,否則容易出 bug。
下面的Class Component例子中,副作用代碼寫在了componentDidMount和componentDidUpdate中:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return (); } }You clicked {this.state.count} times
可以看到componentDidMount和componentDidUpdate中的代碼是一樣的。而使用 Effect Hook 來改寫就不會(huì)有這個(gè)問題:
import React, { useState, useEffect } from "react"; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return (); }You clicked {count} times
useEffect會(huì)在每次 DOM 渲染后執(zhí)行,不會(huì)阻塞頁面渲染。它同時(shí)具備componentDidMount、componentDidUpdate和componentWillUnmount三個(gè)生命周期函數(shù)的執(zhí)行時(shí)機(jī)。
此外還有一些副作用需要組件卸載的時(shí)候做一些額外的清理工作的,例如訂閱某個(gè)功能:
class FriendStatus extends React.Component { constructor(props) { super(props); this.state = { isOnline: null }; this.handleStatusChange = this.handleStatusChange.bind(this); } componentDidMount() { ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); } render() { if (this.state.isOnline === null) { return "Loading..."; } return this.state.isOnline ? "Online" : "Offline"; } }
在componentDidMount訂閱后,需要在componentWillUnmount取消訂閱。使用 Effect Hook 來改寫會(huì)是這個(gè)樣子:
import React, { useState, useEffect } from "react"; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // 返回一個(gè)函數(shù)來進(jìn)行額外的清理工作: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return "Loading..."; } return isOnline ? "Online" : "Offline"; }
當(dāng)useEffect的返回值是一個(gè)函數(shù)的時(shí)候,React 會(huì)在下一次執(zhí)行這個(gè)副作用之前執(zhí)行一遍清理工作,整個(gè)組件的生命周期流程可以這么理解:
組件掛載 --> 執(zhí)行副作用 --> 組件更新 --> 執(zhí)行清理函數(shù) --> 執(zhí)行副作用 --> 組件更新 --> 執(zhí)行清理函數(shù) --> 組件卸載
上文提到useEffect會(huì)在每次渲染后執(zhí)行,但有的情況下我們希望只有在 state 或 props 改變的情況下才執(zhí)行。如果是Class Component,我們會(huì)這么做:
componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `You clicked ${this.state.count} times`; } }
使用 Hook 的時(shí)候,我們只需要傳入第二個(gè)參數(shù):
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 只有在 count 改變的時(shí)候才執(zhí)行 Effect
第二個(gè)參數(shù)是一個(gè)數(shù)組,可以傳多個(gè)值,一般會(huì)將 Effect 用到的所有 props 和 state 都傳進(jìn)去。
當(dāng)副作用只需要在組件掛載的時(shí)候和卸載的時(shí)候執(zhí)行,第二個(gè)參數(shù)可以傳一個(gè)空數(shù)組[],實(shí)現(xiàn)的效果有點(diǎn)類似componentDidMount和componentWillUnmount的組合。
六、總結(jié)本文介紹了在 React 之前版本中存在的一些問題,然后引入 Hooks 的解決方案,并詳細(xì)介紹了 2 個(gè)最重要的 Hooks:useState和useEffect的用法及注意事項(xiàng)。本來想一篇寫完所有相關(guān)的內(nèi)容,但發(fā)現(xiàn)坑有點(diǎn)深,只能分兩次填了:)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103688.html
摘要:第一次了解這項(xiàng)特性的時(shí)候,真的有一種豁然開朗,發(fā)現(xiàn)新大陸的感覺。在絕大多數(shù)情況下,是更好的選擇。唯一例外的就是需要根據(jù)新的來進(jìn)行操作的場(chǎng)景。會(huì)保證在頁面渲染前執(zhí)行,也就是說頁面渲染出來的是最終的效果。上面條規(guī)則都是為了保證調(diào)用順序的穩(wěn)定性。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...
摘要:動(dòng)態(tài)處理與,封裝了在運(yùn)行時(shí)的進(jìn)行一類增加和刪除的操作,例如可以再切換到某一路由時(shí)動(dòng)態(tài)的加入一個(gè)個(gè)人猜測(cè),熱更新很有可能也利用了這個(gè)兩個(gè)與。以上是本人對(duì)于的粗略的理解,內(nèi)容如有錯(cuò)誤,還請(qǐng)大家指出。 寫在前面 dva是螞蟻金服推出的一個(gè)單頁應(yīng)用框架,對(duì)redux,react-router,redux-saga進(jìn)行了上層封裝,沒有引入新的概念,但是極大的程度上提升了開發(fā)效率;下面內(nèi)容為本人理...
這是講 ahooks 源碼的第一篇文章,簡(jiǎn)要就是以下幾點(diǎn): 加深對(duì) React hooks 的理解?! W(xué)習(xí)如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫(kù)?! ∨囵B(yǎng)閱讀學(xué)習(xí)源碼的習(xí)慣,工具庫(kù)是一個(gè)對(duì)源碼閱讀不錯(cuò)的選擇。 注:本系列對(duì) ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對(duì)源碼做了一些解讀,可見詳情?! 〉谝黄饕榻B a...
起因 社會(huì)在不斷的向前,技術(shù)也在不斷的完善進(jìn)步。從 React Hooks 正式發(fā)布到現(xiàn)在,越來越多的項(xiàng)目正在使用 Function Component 替代 Class Component,Hooks 這一新特性也逐漸被廣泛的使用。 這樣的解析是不是很熟悉,在日常中時(shí)常都有用到,但也有一個(gè)可以解決這樣重復(fù)的就是對(duì)數(shù)據(jù)請(qǐng)求的邏輯處理,對(duì)防抖節(jié)流的邏輯處理等。 另一方面,由于 Hoo...
摘要:本文將根據(jù)以下章節(jié)分別梳理每個(gè)鉤子同步鉤子首先安裝是簡(jiǎn)單的同步鉤子,它很類似于發(fā)布訂閱。至此,我們把的所有同步鉤子都解析完畢異步鉤子比同步鉤子麻煩些,我們會(huì)在下一章節(jié)開始解析異步的鉤子傳送門深入理解核心模塊鉤子異步版代碼 記錄下自己在前端路上爬坑的經(jīng)歷 加深印象,正文開始~ tapable是webpack的核心依賴庫(kù) 想要讀懂webpack源碼 就必須首先熟悉tapableok.下面是...
閱讀 2306·2021-11-24 09:39
閱讀 2550·2021-11-22 15:24
閱讀 2989·2021-09-02 09:48
閱讀 3032·2021-07-26 22:01
閱讀 1444·2019-08-30 11:09
閱讀 1683·2019-08-29 18:47
閱讀 615·2019-08-29 15:40
閱讀 2141·2019-08-29 15:22