亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

聽說你還不懂React Hook?

發布時間:2020-06-25 20:49:02 來源:網絡 閱讀:1104 作者:可樂程序員 欄目:大數據

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

從官網的這句話中,我們可以明確的知道,Hook增加了函數式組件中state的使用,在之前函數式組件是無法擁有自己的狀態,只能通過props以及context來渲染自己的UI,而在業務邏輯中,有些場景必須要使用到state,那么我們就只能將函數式組件定義為class組件。而現在通過Hook,我們可以輕松的在函數式組件中維護我們的狀態,不需要更改為class組件。

React Hooks要解決的問題是狀態共享,這里的狀態共享是指只共享狀態邏輯復用,并不是指數據之間的共享。我們知道在React Hooks之前,解決狀態邏輯復用問題,我們通常使用higher-order components和render-props,那么既然已經有了這兩種解決方案,為什么React開發者還要引入React Hook?對于higher-order components和render-props,React Hook的優勢在哪?

React Hook例子

我們先來看一下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?(
?<div>
?<p>You?clicked?{count}?times</p>
?<button?onClick={()?=>?setCount(count?+?1)}>
?Click?me
?</button>
?</div>
?);
}
復制代碼

我們再來看看不用React Hook的話,如何實現

class?Example?extends?React.Component?{
?constructor(props)?{
?super(props);
?this.state?=?{
?count:?0
?};
?}
?render()?{
?return?(
?<div>
?<p>You?clicked?{this.state.count}?times</p>
?<button?onClick={()?=>?this.setState({?count:?this.state.count?+?1?})}>
?Click?me
?</button>
?</div>
?);
?}
}
復制代碼

可以看到,在React Hook中,class Example組件變成了函數式組件,但是這個函數式組件卻擁有的自己的狀態,同時還可以更新自身的狀態。這一切都得益于useState這個Hook,useState 會返回一對值:當前狀態和一個讓你更新它的函數,你可以在事件處理函數中或其他一些地方調用這個函數。它類似 class 組件的 this.setState,但是它不會把新的 state 和舊的 state 進行合并

React復用狀態邏輯的解決方案

Hook是另一種復用狀態邏輯的解決方案,React開發者一直以來對狀態邏輯的復用方案不斷提出以及改進,從Mixin到高階組件到Render Props 到現在的Hook,我們先來簡單了解一下以前的解決方案

Mixin模式

聽說你還不懂React Hook?


在React最早期,提出了根據Mixin模式來復用組件之間的邏輯。在Javascript中,我們可以將Mixin繼承看作是通過擴展收集功能的一種途徑.我們定義的每一個新的對象都有一個原型,從中它可以繼承更多的屬性.原型可以從其他對象繼承而來,但是更重要的是,能夠為任意數量的對象定義屬性.我們可以利用這一事實來促進功能重用。

React中的mixin主要是用于在完全不相關的兩個組件中,有一套基本相似的功能,我們就可以將其提取出來,通過mixin的方式注入,從而實現代碼的復用。例如,在不同的組件中,組件需要每隔一段時間更新一次,我們可以通過創建setInterval()函數來實現這個功能,同時在組件銷毀的時候,我們需要卸載此函數。因此可以創建一個簡單的 mixin,提供一個簡單的 setInterval() 函數,它會在組件被銷毀時被自動清理。

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);?//?調用?mixin?上的方法
?},
?tick:?function()?{
?this.setState({seconds:?this.state.seconds?+?1});
?},
?render:?function()?{
?return?(
?<p>
?React?has?been?running?for?{this.state.seconds}?seconds.
?</p>
?);
?}
});
ReactDOM.render(
?<TickTock?/>,
?document.getElementById('example')
);
復制代碼

mixin的缺點

  1. 不同mixin可能會相互依賴,耦合性太強,導致后期維護成本過高

  2. mixin中的命名可能會沖突,無法使用同一命名的mixin

  3. mixin即使開始很簡單,它們會隨著業務場景增多,時間的推移產生滾雪球式的復雜化

具體缺點可以看此鏈接Mixins是一種禍害

因為mixin的這些缺點存在,在React中已經不建議使用mixin模式來復用代碼,React全面推薦使用高階組件來替代mixin模式,同時ES6本身是不包含任何 mixin 支持。因此,當你在 React 中使用 ES6 class 時,將不支持 mixins 。

聽說你還不懂React Hook?


高階組件

高階組件(HOC)是 React 中用于復用組件邏輯的一種高級技巧。HOC 自身不是 React API 的一部分,它是一種基于 React 的組合特性而形成的設計模式

高級組件并不是React提供的API,而是React的一種運用技巧,高階組件可以看做是裝飾者模式(Decorator Pattern)在React的實現。裝飾者模式: 動態將職責附加到對象上,若要擴展功能,裝飾者提供了比繼承更具彈性的代替方案.

具體而言,高階組件是參數為組件,返回值為新組件的函數。

組件是將 props 轉換為 UI,而高階組件是將組件轉換為另一個組件

我們可以通過高階組件動態給其他組件增加日志打印功能,而不影響原先組件的功能

function?logProps(WrappedComponent)?{
?return?class?extends?React.Component?{
?componentWillReceiveProps(nextProps)?{
?console.log('Current?props:?',?this.props);
?console.log('Next?props:?',?nextProps);
?}
?render()?{
?return?<WrappedComponent?{...this.props}?/>;
?}
?}
}
復制代碼

Render Propss

術語 “Render Props” 是指一種在 React 組件之間使用一個值為函數的 prop 共享代碼的簡單技術

具有 Render Props 的組件接受一個函數,該函數返回一個 React 元素并調用它而不是實現自己的渲染邏輯

以下我們提供了一個帶有prop的<Mouse>組件,它能夠動態決定什么需要渲染,這樣就能對<Mouse>組件的邏輯以及狀態復用,而不用改變它的渲染結構。

class?Cat?extends?React.Component?{
?render()?{
?const?mouse?=?this.props.mouse;
?return?(
?<img?src="/cat.jpg"?style={{?position:?'absolute',?left:?mouse.x,?top:?mouse.y?}}?/>
?);
?}
}
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?(
?<div?style={{?height:?'100%'?}}?onMouseMove={this.handleMouseMove}>
?{this.props.render(this.state)}
?</div>
?);
?}
}
class?MouseTracker?extends?React.Component?{
?render()?{
?return?(
?<div>
?<h2>移動鼠標!</h2>
?<Mouse?render={mouse?=>?(
?
?)}/>
?</div>
?);
?}
}
復制代碼

然而通常我們說的Render Props 是因為模式才被稱為 Render Props ,又不是因為一定要用render對prop進行命名。我們也可以這樣來表示

<Mouse>
?{mouse?=>?(
?<Cat?mouse={mouse}?/>
?)}
</Mouse>
復制代碼

React Hook動機

React Hook是官網提出的又一種全新的解決方案,在了解React Hook之前,我們先看一下React Hook提出的動機

  1. 在組件之間復用狀態邏輯很難

  2. 復雜組件變得難以理解

  3. 難以理解的 class

下面說說我對這三個動機的理解:

在組件之間復用狀態邏輯很難,在之前,我們通過高階組件(Higher-Order Components)和渲染屬性(Render Propss)來解決狀態邏輯復用困難的問題。很多庫都使用這些模式來復用狀態邏輯,比如我們常用redux、React Router。高階組件、渲染屬性都是通過組合來一層層的嵌套共用組件,這會大大增加我們代碼的層級關系,導致層級的嵌套過于夸張。從React的devtool我們可以清楚的看到,使用這兩種模式導致的層級嵌套程度

聽說你還不懂React Hook?


復雜組件變得難以理解,在不斷變化的業務需求中,組件逐漸會被狀態邏輯以及副作用充斥,每個生命周期常常會包含一些不相關的邏輯。我們寫代碼通常都依據函數的單一原則,一個函數一般只處理一件事,但在生命周期鉤子函數中通常會同時做很多事情。比如,在我們需要在componentDidMount中發起ajax請求獲取數據,同時有時候也會把事件綁定寫在此生命周期中,甚至有時候需要在componentWillReceiveProps中對數據進行跟componentDidMount一樣的處理。

相互關聯且需要對照修改的代碼被進行了拆分,而完全不相關的代碼卻在同一個方法中組合在一起。如此很容易產生 bug,并且導致邏輯不一致。

難以理解的class,個人覺得使用class組件這種還是可以的,只要了解了class的this指向綁定問題,其實上手的難度不大。大家要理解,這并不是 React 特有的行為;這其實與 JavaScript 函數工作原理有關。所以只要了解好JS函數工作原理,其實this綁定都不是事。只是有時候為了保證this的指向正確,我們通常會寫很多代碼來綁定this,如果忘記綁定的話,就有會各種bug。綁定this方法:

1.this.handleClick?=?this.handleClick.bind(this);
2.<button?onClick={(e)?=>?this.handleClick(e)}>
?Click?me
?</button>
復制代碼

于是為了解決以上問題,React Hook就被提出來了

state Hook使用

我們回到剛剛的代碼中,看一下如何在函數式組件中定義state

import?React,?{?useState?}?from?'React';
const?[count,?setCount]?=?useState(0);
復制代碼
  1. useState做了啥

  2. 我們可以看到,在此函數中,我們通過useState定義了一個'state變量',它與 class 里面的 this.state 提供的功能完全相同.相當于以下代碼

class?Example?extends?React.Component?{
?constructor(props)?{
?super(props);
?this.state?=?{
?count:?0
?};
?}
復制代碼
  1. useState參數

  2. 在代碼中,我們傳入了0作為useState的參數,這個參數的數值會被當成count初始值。當然此參數不限于傳遞數字以及字符串,可以傳入一個對象當成初始的state。如果state需要儲存多個變量的值,那么調用多次useState即可

  3. useState返回值

  4. 返回值為:當前 state 以及更新 state 的函數,這與 class 里面 this.state.count 和 this.setState 類似,唯一區別就是你需要成對的獲取它們。看到[count, setCount]很容易就能明白這是ES6的解構數組的寫法。相當于以下代碼

let?_useState?=?useState(0);//?返回一個有兩個元素的數組
let?count?=?_useState[0];//?數組里的第一個值
let?setCount?=?_useState[1];//?數組里的第二個值
復制代碼

讀取狀態值

只需要使用變量即可

以前寫法

<p>You?clicked?{this.state.count}?times</p>
復制代碼

現在寫法

<p>You?clicked?{count}?times</p>
復制代碼

更新狀態

通過setCount函數更新

以前寫法

<button?onClick={()?=>?this.setState({?count:?this.state.count?+?1?})}>
?Click?me
?</button>
復制代碼

現在寫法

?<button?onClick={()?=>?setCount(count?+?1)}>
?Click?me
?</button>
復制代碼

這里setCount接收的參數是修改過的新狀態值

聲明多個state變量

我們可以在一個組件中多次使用state Hook來聲明多個state變量

function?ExampleWithManyStates()?{
?//?聲明多個?state?變量!
?const?[age,?setAge]?=?useState(42);
?const?[fruit,?setFruit]?=?useState('banana');
?const?[todos,?setTodos]?=?useState([{?text:?'Learn?Hooks'?}]);
?//?...
}
復制代碼

React 假設當你多次調用 useState 的時候,你能保證每次渲染時它們的調用順序是不變的

為什么React要規定每次渲染它們時的調用順序不變呢,這個是一個理解Hook至關重要的問題

Hook 規則

Hook 本質就是 JavaScript 函數,但是在使用它時需要遵循兩條規則。并且React要求強制執行這兩條規則,不然就會出現異常的bug

  1. 只在最頂層使用 Hook

不要在循環,條件或嵌套函數中調用 Hook,?確保總是在你的 React 函數的最頂層調用他們

  1. 只在 React 函數中調用 Hook

不要在普通的 JavaScript 函數中調用 Hook

這兩條規則出現的原因是,我們可以在單個組件中使用多個State Hook 或 Effect Hook,React 靠的是 Hook 調用的順序來知道哪個 state 對應哪個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(參數被忽略)
useState('Arzh3')?//?2.?讀取變量名為?name2?的?state(參數被忽略)
useEffect('Arzh4')?//?3.?讀取變量名為?name3?的?state(參數被忽略)
復制代碼

如果我們違反React的規則,使用條件渲染

if?(name?!==?'')?{
?const?[name2,?setName2]?=?useState('Arzh3');
}
復制代碼

假設第一次(name !== '')為true的時候,執行此Hook,第二次渲染(name !== '')為false時,不執行此Hook,那么Hook的調用順序就會發生變化,產生bug

useState('Arzh2')?//?1.?讀取變量名為?name1?的?state
//useState('Arzh3')?//?2.?Hook被忽略
useEffect('Arzh4')?//?3.?讀取變量名為?name2(之前為name3)?的?state
復制代碼

React 不知道第二個 useState 的 Hook 應該返回什么。React 會以為在該組件中第二個 Hook 的調用像上次的渲染一樣,對應的是 arzh3 的 useState,但并非如此。所以這就是為什么React強制要求Hook使用必須遵循這兩個規則,同時我們可以使用 eslint-plugin-React-Hooks來強制約束

Effect Hook使用

我們在上面的代碼中增加Effect Hook的使用,在函數式組件中增加副作用,修改網頁的標題

?useEffect(()?=>?{
?document.title?=?`You?clicked?${count}?times`;
?});
復制代碼

如果你熟悉 React class 的生命周期函數,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

也就是我們完全可以通過useEffect來替代這三個生命鉤子函數

我們來了解一下通常需要副作用的場景,比如發送請求,手動變更dom,記錄日志等。通常我們都會在第一次dom渲染完成以及后續dom重新更新時,去調用我們的副作用操作。我們可以看一下以前生命周期的實現

?componentDidMount()?{
?document.title?=?`You?clicked?${this.state.count}?times`;
?}
?componentDidUpdate()?{
?document.title?=?`You?clicked?${this.state.count}?times`;
?}
復制代碼

這也就是我們上面提到的React Hook動機的第二個問題來源之一,需要在第一次渲染以及后續的渲染中調用相同的代碼

Effect在默認情況下,會在第一次渲染之后每次更新之后都會執行,這也就讓我們不需要再去考慮是componentDidMount還是componentDidUpdate時執行,只需要明白Effect在組件渲染后執行即可

清除副作用

有時候對于一些副作用,我們是需要去清除的,比如我們有個需求需要輪詢向服務器請求最新狀態,那么我們就需要在卸載的時候,清理掉輪詢的操作。

?componentDidMount()?{
?this.pollingNewStatus()
?}
?componentWillUnmount()?{
?this.unPollingNewStatus()
?}
復制代碼

我們可以使用Effect來清除這些副作用,只需要在Effect中返回一個函數即可

?useEffect(()?=>?{
?pollingNewStatus()
?//告訴React在每次渲染之前都先執行cleanup()
?return?function?cleanup()?{
?unPollingNewStatus()
?};
?});
復制代碼

有個明顯的區別在于useEffect其實是每次渲染之前都會去執行cleanup(),而componentWillUnmount只會執行一次。

Effect性能優化

useEffect其實是每次更新都會執行,在某些情況下會導致性能問題。那么我們可以通過跳過 Effect 進行性能優化。在class組件中,我們可以通過在 componentDidUpdate 中添加對 prevProps 或 prevState 的比較邏輯解決

componentDidUpdate(prevProps,?prevState)?{
?if?(prevState.count?!==?this.state.count)?{
?document.title?=?`You?clicked?${this.state.count}?times`;
?}
}
復制代碼

在Effect中,我們可以通過增加Effect的第二個參數即可,如果沒有變化,則跳過更新

useEffect(()?=>?{
?document.title?=?`You?clicked?${count}?times`;
},?[count]);?//?僅在?count?更改時更新

有更多資料視頻,加小可樂丫

聽說你還不懂React Hook?


聽說你還不懂React Hook?


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

榆林市| 涟源市| 日土县| 石屏县| 仙游县| 和龙市| 乳山市| 永城市| 青海省| 互助| 鹤山市| 分宜县| 都兰县| 西充县| 蕲春县| 灵台县| 宝兴县| 麻江县| 景宁| 崇明县| 洮南市| 青海省| 额济纳旗| 山西省| 南阳市| 南通市| 于都县| 德兴市| 舒兰市| 定西市| 贵溪市| 北辰区| 大连市| 西和县| 兰考县| 连山| 乌拉特中旗| 千阳县| 玉山县| 汽车| 玉环县|