咱們都知道,在React中一切都是組件,而組件的建立方式有多種。若是你是久經戰場的React開發者,那你確定知道createclass建立組件的方式,可是這種方式已通過時了。目前經常使用的兩種建立組件的方式是使用es6的class來建立組件,咱們稱爲類組件,以及使用普通的函數來建立組件,咱們稱之爲函數組件。在React V16.8以前,類組件和函數組件有着不一樣的功能,所以也適用於不一樣的適用場景。可是由於功能比較接近而又有着差別,所以讓不少人難以抉擇使用哪一種方式來建立組件。 例如,咱們在建立純展現組件的時候,咱們使用類組件建立方式以下:react
class Hello extends React.Component {
render() {
return <div>Hello</div>;
}
}
複製代碼
使用函數組件建立方式以下:es6
const Hello = () => {
return <div>Hello</div>;
};
複製代碼
顯而易見,使用class的方式建立該組件顯得大材小用,所以在這種狀況下,咱們更推薦使用函數組件來建立。編程
那麼函數組件有哪些優勢呢?數組
既然函數組件有那麼多的優勢,那麼爲何咱們還須要類組件呢?緩存
而以上功能,在函數式組件中是不存在的,所以咱們必須使用類組件。知道React v16.8,React給咱們帶來了hooks,hooks就是用來提供類組件中有而函數組件中沒有的功能。bash
React提供的hooks有不少,可是如下幾種基本涵蓋了咱們業務中百分之八九十的場景:react-router
咱們從命名中能夠看出,hooks採用約定的方式以use開頭,這讓我想起了HOC,HOC每每都是以with開頭,就像withRouter同樣,咱們的組件賦予更強的能力。app
這就是hooks的由來,這就是hooks,那麼hooks怎麼用呢?函數式編程
const Counter = () => {
const [count, setCount] = React.useState(0);
const add = () => { setCount(count + 1); };
return (
<React.Fragment>
<h1>{count}</h1>
<button onClick={add}>add</button>
</React.Fragment>
);
};
複製代碼
上面代碼中,讓咱們感到很陌生的就是第二行代碼:函數
const [count, setCount] = React.useState(0);
複製代碼
那麼咱們來簡單分析一下useState。
在使用useState的時候,咱們須要先初始化,即給useState傳入一個初始值0,useState會給咱們返回一個元組。元組的存在就是讓咱們方便解構,元組的第一個數據是一個數值,即咱們定義的state值,第二個數據是修改該state值的方法。這兩個值一一對應,且與外界沒有任何關係。
這樣咱們在add方法中執行修改state的方法時,即可設置state的數據。
這就是useState的簡單使用。
在咱們的業務場景中,咱們每每須要定義多個state,那麼咱們能夠在函數的開頭依次定義。
const Counter = () => {
const [count, setCount] = React.useState(0);
const [count1, setCount1] = React.useState(10);
const add = () => { setCount(count + 1); };
const minus = () => { setCount(count1 - 1); };
return (
<React.Fragment>
<h1>{count}</h1>
<button onClick={add}>add</button>
<h1>{count1}</h1>
<button onClick={minus}>minus</button>
</React.Fragment>
);
};
複製代碼
寫到這,確定有人會問,一個方法怎麼保存狀態?一個函數組件怎麼保存狀態?那麼咱們來分析下useState的原理。 咱們都知道,每個組件都對應一個Fiber對象,每一個Fiber對象中都會有一個memorizeState的屬性來存儲組件內的狀態。例如一個類組件中的全部狀態都存儲在Fiber對象的memorizeState中。而對於一個函數組件而言,當第一調用useState的時候,React會給這第一個state生成一個hooks對象,這個hooks對象就指向了Fiber對象的memorizeState屬性,所以Fiber中的memorizeState屬性也能夠用來存儲函數組件的狀態。那麼咱們來看看hooks對象:
{
baseState,
next,
baseUpdate,
queue,
memoizedState
}
複製代碼
咱們能夠看到,hooks對象中也包含一個memorizedState屬性,這個屬性就是用來存儲當前的state的值。同時包含一個next屬性,這個next屬性就是用來指向下一次執行useState時生成的hooks對象,以此類推,按照函數組件第一次執行時useState的初始化順序生成一個hooks對象的調用鏈,之後按照這個順序依次取值。說到這裏,useState中的調用順序相當重要,加入咱們的代碼是這樣的
const Counter = () => {
const [count, setCount] = React.useState(0);
if (count === 0) {
const [count1, setCount1] = React.useState(1);
}
const [count2, setCount2] = React.useState(2);
複製代碼
當函數組件第一次執行的時候,默認會初始化三個useState。順序以下:
useState1 => memoizedState count = useState1.memoizedState
useState1.next => useState2 count2 = useState2.memoizedState
useState2.next => useState3 count3 = useState3.memoizedState
當咱們進行一系列操做以後,count的值由0變成其它數值了而且致使函數組件從新更新了,那麼此時useState再次執行,執行順序以下:
useState1 => memoizedState count === useState1.memoizedState
useState1.next => useState2 count2 === useState2.memoizedState
此時會把state1的值取出來賦值給count2,致使bug的出現。
所以,在使用useState時,相當重要的就是不管執行多少次,useState的執行順序和執行數量都保持一致。
useEffect能夠說是componentDidmount和compoenntDidUpdate的結合體,咱們能夠簡單列下useEffect如何達到這兩種效果。
useEffect(() => {
// mount時會調用
},[])
複製代碼
其中第二個參數能夠傳一個數組,若是數組爲空,則只在componentDidMount的時候執行。若是傳入一個state,當該state變化致使頁面從新渲染時,useEffect也會執行,達到componentDidUpdate的效果。
useEffect(() => {
// mount或者update都會調用
})
複製代碼
const mounted = React.useRef();
React.useEffect(() => {
if (!mounted.current) {
mounted.current = true;
} else {
// update操做
}
});
複製代碼
咱們可使用React提供的另一種hooks:useRef來實現componentDidUpdate的效果。useRef會返回一個對象,該對象包含一個current屬性,useRef主要用來存儲整個生命週期中本身須要緩存的數據。當組件mount的時候,current是undefined,此時咱們給current賦值爲true,這樣在之後update的時候,current值永遠爲true,達到了只在componentDidUpdate時執行的效果。
useEffect(() => {
// mount時會調用
return () => {
// unmount時會調用
}
},[])
複製代碼
useEffect的第一個參數能夠return一個方法,這個方法就用於在componentDidUnMount時執行。
const useAdd = initValue => {
const [count, setCount] = React.useState(initValue);
const addCount = () => {
setCount(count + 1);
};
return [count, addCount];
};
const Counter = () => {
const [count, setCount] = useAdd(0);
const Counter = () => {
const [count, setCount] = useAdd(0);
複製代碼
以上例子,咱們能夠將操做加這個行爲的邏輯和數據封裝成一個useAdd hook,而後在各組件中靈活複用,提升了代碼的利用率,減小了冗餘代碼。
<Wrapper render={({clickEvent}) => (
<button onClick={clickEvent} />
)}/>
複製代碼
另一種方式是HOC,例如withRouter將react-router 的 history、location、match 三個對象傳入prop。而hooks的custome hooks能夠爲咱們更好的複用組件。
React v16.8帶來的hooks必定程度上給咱們提供了巨大的便利,提供了咱們的工做效率,減小了冗餘的代碼。hooks也是一種趨勢,業界也在一直擁抱hooks,所以若是你是react的忠實用戶,勇敢使用hooks吧!