Hook 是 React 16.8 的新增特性。它可以讓你在不編寫(xiě) class 的情況下使用 state 以及其他的 React 特性
成都創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),蒼溪企業(yè)網(wǎng)站建設(shè),蒼溪品牌網(wǎng)站建設(shè),網(wǎng)站定制,蒼溪網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,蒼溪網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
從官網(wǎng)的這句話中,我們可以明確的知道,Hook增加了函數(shù)式組件中state的使用,在之前函數(shù)式組件是無(wú)法擁有自己的狀態(tài),只能通過(guò)props以及context來(lái)渲染自己的UI,而在業(yè)務(wù)邏輯中,有些場(chǎng)景必須要使用到state,那么我們就只能將函數(shù)式組件定義為class組件。而現(xiàn)在通過(guò)Hook,我們可以輕松的在函數(shù)式組件中維護(hù)我們的狀態(tài),不需要更改為class組件。
React Hooks要解決的問(wèn)題是狀態(tài)共享,這里的狀態(tài)共享是指只共享狀態(tài)邏輯復(fù)用,并不是指數(shù)據(jù)之間的共享。我們知道在React Hooks之前,解決狀態(tài)邏輯復(fù)用問(wèn)題,我們通常使用higher-order components和render-props,那么既然已經(jīng)有了這兩種解決方案,為什么React開(kāi)發(fā)者還要引入React Hook?對(duì)于higher-order components和render-props,React Hook的優(yōu)勢(shì)在哪?
React Hook例子
我們先來(lái)看一下React官方給出的React Hook的demo
import?{?useState?}?from?'React'; function?Example()?{ ?//?Declare?a?new?state?variable,?which?we'll?call?"count" ?const?[count,?setCount]?=?useState(0); ?return?( ???); } 復(fù)制代碼You?clicked?{count}?times
? ?
我們?cè)賮?lái)看看不用React Hook的話,如何實(shí)現(xiàn)
class?Example?extends?React.Component?{ ?constructor(props)?{ ?super(props); ?this.state?=?{ ?count:?0 ?}; ?} ?render()?{ ?return?( ???); ?} } 復(fù)制代碼You?clicked?{this.state.count}?times
? ?
可以看到,在React Hook中,class Example組件變成了函數(shù)式組件,但是這個(gè)函數(shù)式組件卻擁有的自己的狀態(tài),同時(shí)還可以更新自身的狀態(tài)。這一切都得益于useState這個(gè)Hook,useState 會(huì)返回一對(duì)值:當(dāng)前狀態(tài)和一個(gè)讓你更新它的函數(shù),你可以在事件處理函數(shù)中或其他一些地方調(diào)用這個(gè)函數(shù)。它類似 class 組件的 this.setState,但是它不會(huì)把新的 state 和舊的 state 進(jìn)行合并
React復(fù)用狀態(tài)邏輯的解決方案
Hook是另一種復(fù)用狀態(tài)邏輯的解決方案,React開(kāi)發(fā)者一直以來(lái)對(duì)狀態(tài)邏輯的復(fù)用方案不斷提出以及改進(jìn),從Mixin到高階組件到Render Props 到現(xiàn)在的Hook,我們先來(lái)簡(jiǎn)單了解一下以前的解決方案
Mixin模式
在React最早期,提出了根據(jù)Mixin模式來(lái)復(fù)用組件之間的邏輯。在Javascript中,我們可以將Mixin繼承看作是通過(guò)擴(kuò)展收集功能的一種途徑.我們定義的每一個(gè)新的對(duì)象都有一個(gè)原型,從中它可以繼承更多的屬性.原型可以從其他對(duì)象繼承而來(lái),但是更重要的是,能夠?yàn)槿我鈹?shù)量的對(duì)象定義屬性.我們可以利用這一事實(shí)來(lái)促進(jìn)功能重用。
React中的mixin主要是用于在完全不相關(guān)的兩個(gè)組件中,有一套基本相似的功能,我們就可以將其提取出來(lái),通過(guò)mixin的方式注入,從而實(shí)現(xiàn)代碼的復(fù)用。例如,在不同的組件中,組件需要每隔一段時(shí)間更新一次,我們可以通過(guò)創(chuàng)建setInterval()函數(shù)來(lái)實(shí)現(xiàn)這個(gè)功能,同時(shí)在組件銷毀的時(shí)候,我們需要卸載此函數(shù)。因此可以創(chuàng)建一個(gè)簡(jiǎn)單的 mixin,提供一個(gè)簡(jiǎn)單的 setInterval() 函數(shù),它會(huì)在組件被銷毀時(shí)被自動(dòng)清理。
var?SetIntervalMixin?=?{ ?componentWillMount:?function()?{ ?this.intervals?=?[]; ?}, ?setInterval:?function()?{ ?this.intervals.push(setInterval.apply(null,?arguments)); ?}, ?componentWillUnmount:?function()?{ ?this.intervals.forEach(clearInterval); ?} }; var?createReactClass?=?require('create-React-class'); var?TickTock?=?createReactClass({ ?mixins:?[SetIntervalMixin],?//?使用?mixin ?getInitialState:?function()?{ ?return?{seconds:?0}; ?}, ?componentDidMount:?function()?{ ?this.setInterval(this.tick,?1000);?//?調(diào)用?mixin?上的方法 ?}, ?tick:?function()?{ ?this.setState({seconds:?this.state.seconds?+?1}); ?}, ?render:?function()?{ ?return?( ??React?has?been?running?for?{this.state.seconds}?seconds. ?
?); ?} }); ReactDOM.render( ?, ?document.getElementById('example') ); 復(fù)制代碼
mixin的缺點(diǎn)
不同mixin可能會(huì)相互依賴,耦合性太強(qiáng),導(dǎo)致后期維護(hù)成本過(guò)高
mixin中的命名可能會(huì)沖突,無(wú)法使用同一命名的mixin
mixin即使開(kāi)始很簡(jiǎn)單,它們會(huì)隨著業(yè)務(wù)場(chǎng)景增多,時(shí)間的推移產(chǎn)生滾雪球式的復(fù)雜化
具體缺點(diǎn)可以看此鏈接Mixins是一種禍害
因?yàn)閙ixin的這些缺點(diǎn)存在,在React中已經(jīng)不建議使用mixin模式來(lái)復(fù)用代碼,React全面推薦使用高階組件來(lái)替代mixin模式,同時(shí)ES6本身是不包含任何 mixin 支持。因此,當(dāng)你在 React 中使用 ES6 class 時(shí),將不支持 mixins 。
高階組件
高階組件(HOC)是 React 中用于復(fù)用組件邏輯的一種高級(jí)技巧。HOC 自身不是 React API 的一部分,它是一種基于 React 的組合特性而形成的設(shè)計(jì)模式
高級(jí)組件并不是React提供的API,而是React的一種運(yùn)用技巧,高階組件可以看做是裝飾者模式(Decorator Pattern)在React的實(shí)現(xiàn)。裝飾者模式: 動(dòng)態(tài)將職責(zé)附加到對(duì)象上,若要擴(kuò)展功能,裝飾者提供了比繼承更具彈性的代替方案.
具體而言,高階組件是參數(shù)為組件,返回值為新組件的函數(shù)。
組件是將 props 轉(zhuǎn)換為 UI,而高階組件是將組件轉(zhuǎn)換為另一個(gè)組件
我們可以通過(guò)高階組件動(dòng)態(tài)給其他組件增加日志打印功能,而不影響原先組件的功能
function?logProps(WrappedComponent)?{ ?return?class?extends?React.Component?{ ?componentWillReceiveProps(nextProps)?{ ?console.log('Current?props:?',?this.props); ?console.log('Next?props:?',?nextProps); ?} ?render()?{ ?return?; ?} ?} } 復(fù)制代碼
Render Propss
術(shù)語(yǔ) “Render Props” 是指一種在 React 組件之間使用一個(gè)值為函數(shù)的 prop 共享代碼的簡(jiǎn)單技術(shù)
具有 Render Props 的組件接受一個(gè)函數(shù),該函數(shù)返回一個(gè) React 元素并調(diào)用它而不是實(shí)現(xiàn)自己的渲染邏輯
以下我們提供了一個(gè)帶有prop的
class?Cat?extends?React.Component?{ ?render()?{ ?const?mouse?=?this.props.mouse; ?return?( ? ?); ?} } class?Mouse?extends?React.Component?{ ?constructor(props)?{ ?super(props); ?this.handleMouseMove?=?this.handleMouseMove.bind(this); ?this.state?=?{?x:?0,?y:?0?}; ?} ?handleMouseMove(event)?{ ?this.setState({ ?x:?event.clientX, ?y:?event.clientY ?}); ?} ?render()?{ ?return?( ??{this.props.render(this.state)} ??); ?} } class?MouseTracker?extends?React.Component?{ ?render()?{ ?return?( ???); ?} } 復(fù)制代碼移動(dòng)鼠標(biāo)!
??( ? ?)}/> ?
然而通常我們說(shuō)的Render Props 是因?yàn)槟J讲疟环Q為 Render Props ,又不是因?yàn)橐欢ㄒ胷ender對(duì)prop進(jìn)行命名。我們也可以這樣來(lái)表示
?{mouse?=>?( ? 復(fù)制代碼?)}
React Hook動(dòng)機(jī)
React Hook是官網(wǎng)提出的又一種全新的解決方案,在了解React Hook之前,我們先看一下React Hook提出的動(dòng)機(jī)
在組件之間復(fù)用狀態(tài)邏輯很難
復(fù)雜組件變得難以理解
難以理解的 class
下面說(shuō)說(shuō)我對(duì)這三個(gè)動(dòng)機(jī)的理解:
在組件之間復(fù)用狀態(tài)邏輯很難,在之前,我們通過(guò)高階組件(Higher-Order Components)和渲染屬性(Render Propss)來(lái)解決狀態(tài)邏輯復(fù)用困難的問(wèn)題。很多庫(kù)都使用這些模式來(lái)復(fù)用狀態(tài)邏輯,比如我們常用redux、React Router。高階組件、渲染屬性都是通過(guò)組合來(lái)一層層的嵌套共用組件,這會(huì)大大增加我們代碼的層級(jí)關(guān)系,導(dǎo)致層級(jí)的嵌套過(guò)于夸張。從React的devtool我們可以清楚的看到,使用這兩種模式導(dǎo)致的層級(jí)嵌套程度
復(fù)雜組件變得難以理解,在不斷變化的業(yè)務(wù)需求中,組件逐漸會(huì)被狀態(tài)邏輯以及副作用充斥,每個(gè)生命周期常常會(huì)包含一些不相關(guān)的邏輯。我們寫(xiě)代碼通常都依據(jù)函數(shù)的單一原則,一個(gè)函數(shù)一般只處理一件事,但在生命周期鉤子函數(shù)中通常會(huì)同時(shí)做很多事情。比如,在我們需要在componentDidMount中發(fā)起ajax請(qǐng)求獲取數(shù)據(jù),同時(shí)有時(shí)候也會(huì)把事件綁定寫(xiě)在此生命周期中,甚至有時(shí)候需要在componentWillReceiveProps中對(duì)數(shù)據(jù)進(jìn)行跟componentDidMount一樣的處理。
相互關(guān)聯(lián)且需要對(duì)照修改的代碼被進(jìn)行了拆分,而完全不相關(guān)的代碼卻在同一個(gè)方法中組合在一起。如此很容易產(chǎn)生 bug,并且導(dǎo)致邏輯不一致。
難以理解的class,個(gè)人覺(jué)得使用class組件這種還是可以的,只要了解了class的this指向綁定問(wèn)題,其實(shí)上手的難度不大。大家要理解,這并不是 React 特有的行為;這其實(shí)與 JavaScript 函數(shù)工作原理有關(guān)。所以只要了解好JS函數(shù)工作原理,其實(shí)this綁定都不是事。只是有時(shí)候?yàn)榱吮WCthis的指向正確,我們通常會(huì)寫(xiě)很多代碼來(lái)綁定this,如果忘記綁定的話,就有會(huì)各種bug。綁定this方法:
1.this.handleClick?=?this.handleClick.bind(this); 2. 復(fù)制代碼
于是為了解決以上問(wèn)題,React Hook就被提出來(lái)了
state Hook使用
我們回到剛剛的代碼中,看一下如何在函數(shù)式組件中定義state
import?React,?{?useState?}?from?'React'; const?[count,?setCount]?=?useState(0); 復(fù)制代碼
useState做了啥
我們可以看到,在此函數(shù)中,我們通過(guò)useState定義了一個(gè)'state變量',它與 class 里面的 this.state 提供的功能完全相同.相當(dāng)于以下代碼
class?Example?extends?React.Component?{ ?constructor(props)?{ ?super(props); ?this.state?=?{ ?count:?0 ?}; ?} 復(fù)制代碼
useState參數(shù)
在代碼中,我們傳入了0作為useState的參數(shù),這個(gè)參數(shù)的數(shù)值會(huì)被當(dāng)成count初始值。當(dāng)然此參數(shù)不限于傳遞數(shù)字以及字符串,可以傳入一個(gè)對(duì)象當(dāng)成初始的state。如果state需要儲(chǔ)存多個(gè)變量的值,那么調(diào)用多次useState即可
useState返回值
返回值為:當(dāng)前 state 以及更新 state 的函數(shù),這與 class 里面 this.state.count 和 this.setState 類似,唯一區(qū)別就是你需要成對(duì)的獲取它們。看到[count, setCount]很容易就能明白這是ES6的解構(gòu)數(shù)組的寫(xiě)法。相當(dāng)于以下代碼
let?_useState?=?useState(0);//?返回一個(gè)有兩個(gè)元素的數(shù)組 let?count?=?_useState[0];//?數(shù)組里的第一個(gè)值 let?setCount?=?_useState[1];//?數(shù)組里的第二個(gè)值 復(fù)制代碼
讀取狀態(tài)值
只需要使用變量即可
以前寫(xiě)法
You?clicked?{this.state.count}?times
復(fù)制代碼
現(xiàn)在寫(xiě)法
You?clicked?{count}?times
復(fù)制代碼
更新?tīng)顟B(tài)
通過(guò)setCount函數(shù)更新
以前寫(xiě)法
復(fù)制代碼
現(xiàn)在寫(xiě)法
? 復(fù)制代碼
這里setCount接收的參數(shù)是修改過(guò)的新?tīng)顟B(tài)值
聲明多個(gè)state變量
我們可以在一個(gè)組件中多次使用state Hook來(lái)聲明多個(gè)state變量
function?ExampleWithManyStates()?{ ?//?聲明多個(gè)?state?變量! ?const?[age,?setAge]?=?useState(42); ?const?[fruit,?setFruit]?=?useState('banana'); ?const?[todos,?setTodos]?=?useState([{?text:?'Learn?Hooks'?}]); ?//?... } 復(fù)制代碼
React 假設(shè)當(dāng)你多次調(diào)用 useState 的時(shí)候,你能保證每次渲染時(shí)它們的調(diào)用順序是不變的
為什么React要規(guī)定每次渲染它們時(shí)的調(diào)用順序不變呢,這個(gè)是一個(gè)理解Hook至關(guān)重要的問(wèn)題
Hook 規(guī)則
Hook 本質(zhì)就是 JavaScript 函數(shù),但是在使用它時(shí)需要遵循兩條規(guī)則。并且React要求強(qiáng)制執(zhí)行這兩條規(guī)則,不然就會(huì)出現(xiàn)異常的bug
只在最頂層使用 Hook
不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook,?確??偸窃谀愕?React 函數(shù)的最頂層調(diào)用他們
只在 React 函數(shù)中調(diào)用 Hook
不要在普通的 JavaScript 函數(shù)中調(diào)用 Hook
這兩條規(guī)則出現(xiàn)的原因是,我們可以在單個(gè)組件中使用多個(gè)State Hook 或 Effect Hook,React 靠的是 Hook 調(diào)用的順序來(lái)知道哪個(gè) state 對(duì)應(yīng)哪個(gè)useState
function?Form()?{ ?const?[name1,?setName1]?=?useState('Arzh2'); ?const?[name2,?setName2]?=?useState('Arzh3'); ?const?[name3,?setName3]?=?useState('Arzh4'); ?//?... } //?------------ //?首次渲染 //?------------ useState('Arzh2')?//?1.?使用?'Arzh2'?初始化變量名為?name1?的?state useState('Arzh3')?//?2.?使用?'Arzh3'?初始化變量名為?name2?的?state useEffect('Arzh4')? //?3.?使用?'Arzh4'?初始化變量名為?name3?的?state //?------------- //?二次渲染 //?------------- useState('Arzh2')?//?1.?讀取變量名為?name1?的?state(參數(shù)被忽略) useState('Arzh3')?//?2.?讀取變量名為?name2?的?state(參數(shù)被忽略) useEffect('Arzh4')?//?3.?讀取變量名為?name3?的?state(參數(shù)被忽略) 復(fù)制代碼
如果我們違反React的規(guī)則,使用條件渲染
if?(name?!==?'')?{ ?const?[name2,?setName2]?=?useState('Arzh3'); } 復(fù)制代碼
假設(shè)第一次(name !== '')為true的時(shí)候,執(zhí)行此Hook,第二次渲染(name !== '')為false時(shí),不執(zhí)行此Hook,那么Hook的調(diào)用順序就會(huì)發(fā)生變化,產(chǎn)生bug
useState('Arzh2')?//?1.?讀取變量名為?name1?的?state //useState('Arzh3')?//?2.?Hook被忽略 useEffect('Arzh4')?//?3.?讀取變量名為?name2(之前為name3)?的?state 復(fù)制代碼
React 不知道第二個(gè) useState 的 Hook 應(yīng)該返回什么。React 會(huì)以為在該組件中第二個(gè) Hook 的調(diào)用像上次的渲染一樣,對(duì)應(yīng)的是 arzh3 的 useState,但并非如此。所以這就是為什么React強(qiáng)制要求Hook使用必須遵循這兩個(gè)規(guī)則,同時(shí)我們可以使用 eslint-plugin-React-Hooks來(lái)強(qiáng)制約束
Effect Hook使用
我們?cè)谏厦娴拇a中增加Effect Hook的使用,在函數(shù)式組件中增加副作用,修改網(wǎng)頁(yè)的標(biāo)題
?useEffect(()?=>?{ ?document.title?=?`You?clicked?${count}?times`; ?}); 復(fù)制代碼
如果你熟悉 React class 的生命周期函數(shù),你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個(gè)函數(shù)的組合。
也就是我們完全可以通過(guò)useEffect來(lái)替代這三個(gè)生命鉤子函數(shù)
我們來(lái)了解一下通常需要副作用的場(chǎng)景,比如發(fā)送請(qǐng)求,手動(dòng)變更dom,記錄日志等。通常我們都會(huì)在第一次dom渲染完成以及后續(xù)dom重新更新時(shí),去調(diào)用我們的副作用操作。我們可以看一下以前生命周期的實(shí)現(xiàn)
?componentDidMount()?{ ?document.title?=?`You?clicked?${this.state.count}?times`; ?} ?componentDidUpdate()?{ ?document.title?=?`You?clicked?${this.state.count}?times`; ?} 復(fù)制代碼
這也就是我們上面提到的React Hook動(dòng)機(jī)的第二個(gè)問(wèn)題來(lái)源之一,需要在第一次渲染以及后續(xù)的渲染中調(diào)用相同的代碼
Effect在默認(rèn)情況下,會(huì)在第一次渲染之后和每次更新之后都會(huì)執(zhí)行,這也就讓我們不需要再去考慮是componentDidMount還是componentDidUpdate時(shí)執(zhí)行,只需要明白Effect在組件渲染后執(zhí)行即可
清除副作用
有時(shí)候?qū)τ谝恍└弊饔?,我們是需要去清除的,比如我們有個(gè)需求需要輪詢向服務(wù)器請(qǐng)求最新?tīng)顟B(tài),那么我們就需要在卸載的時(shí)候,清理掉輪詢的操作。
?componentDidMount()?{ ?this.pollingNewStatus() ?} ?componentWillUnmount()?{ ?this.unPollingNewStatus() ?} 復(fù)制代碼
我們可以使用Effect來(lái)清除這些副作用,只需要在Effect中返回一個(gè)函數(shù)即可
?useEffect(()?=>?{ ?pollingNewStatus() ?//告訴React在每次渲染之前都先執(zhí)行cleanup() ?return?function?cleanup()?{ ?unPollingNewStatus() ?}; ?}); 復(fù)制代碼
有個(gè)明顯的區(qū)別在于useEffect其實(shí)是每次渲染之前都會(huì)去執(zhí)行cleanup(),而componentWillUnmount只會(huì)執(zhí)行一次。
Effect性能優(yōu)化
useEffect其實(shí)是每次更新都會(huì)執(zhí)行,在某些情況下會(huì)導(dǎo)致性能問(wèn)題。那么我們可以通過(guò)跳過(guò) Effect 進(jìn)行性能優(yōu)化。在class組件中,我們可以通過(guò)在 componentDidUpdate 中添加對(duì) prevProps 或 prevState 的比較邏輯解決
componentDidUpdate(prevProps,?prevState)?{ ?if?(prevState.count?!==?this.state.count)?{ ?document.title?=?`You?clicked?${this.state.count}?times`; ?} } 復(fù)制代碼
在Effect中,我們可以通過(guò)增加Effect的第二個(gè)參數(shù)即可,如果沒(méi)有變化,則跳過(guò)更新
useEffect(()?=>?{ ?document.title?=?`You?clicked?${count}?times`; },?[count]);?//?僅在?count?更改時(shí)更新
有更多資料視頻,加小可樂(lè)丫
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。