React hooks的使用小結

什麼是hooks

咱們都知道,在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的方式建立該組件顯得大材小用,所以在這種狀況下,咱們更推薦使用函數組件來建立。編程

那麼函數組件有哪些優勢呢?數組

  • 簡單易懂。從上面能夠看出來,函數式組件相比類組件簡單易懂,並且編譯出來的代碼相比類組件更簡單,由於類組件在在編譯過程當中須要將es6的class轉爲es5,同時須要繼承React.Component。而函數組件只需將箭頭函數轉爲普通的函數。
  • 更符合React UI=f(state)的哲學。函數組件更符合UI=f(state)的哲學,由於React自己就是一個畫UI的庫,f就是咱們編寫的組件,state就是咱們的數據,咱們只關心數據傳到咱們的組件中獲得咱們想要的界面。而類組件的方式並不符合這種哲學。
  • 更符合函數式編程的思想。函數式編程的思想是近年逐漸火起來的一種編程範式,React也一種在推崇函數式編程的思想,但不管如何都沒法徹底作到。函數式編程中純函數、不可變數據、函數組合、聲明式的特性也給代碼減小了bug,提供了更好的代碼穩定性。而函數組件更符合這種思想。

既然函數組件有那麼多的優勢,那麼爲何咱們還須要類組件呢?緩存

  • 組件須要state狀態
  • 組件須要例如shouldComponentUpdate等生命週期
  • 組件須要在生命週期中進行反作用

而以上功能,在函數式組件中是不存在的,所以咱們必須使用類組件。知道React v16.8,React給咱們帶來了hooks,hooks就是用來提供類組件中有而函數組件中沒有的功能。bash

React提供的hooks有不少,可是如下幾種基本涵蓋了咱們業務中百分之八九十的場景:react-router

  • useState
  • useEffect
  • custome hooks

咱們從命名中能夠看出,hooks採用約定的方式以use開頭,這讓我想起了HOC,HOC每每都是以with開頭,就像withRouter同樣,咱們的組件賦予更強的能力。app

這就是hooks的由來,這就是hooks,那麼hooks怎麼用呢?函數式編程

hooks的簡單使用

useState的使用

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的使用

useEffect能夠說是componentDidmount和compoenntDidUpdate的結合體,咱們能夠簡單列下useEffect如何達到這兩種效果。

  1. componentDidMount
useEffect(() => {
  // mount時會調用
},[])

複製代碼

其中第二個參數能夠傳一個數組,若是數組爲空,則只在componentDidMount的時候執行。若是傳入一個state,當該state變化致使頁面從新渲染時,useEffect也會執行,達到componentDidUpdate的效果。

  1. componentDidUpdate 和 componentDidMount
useEffect(() => {
  // mount或者update都會調用
})
複製代碼
  1. 模擬componentDidUpdate
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時執行的效果。

  1. componentDidUnMount
useEffect(() => {
  // mount時會調用
  return () => {
    // unmount時會調用
  }
},[])
複製代碼

useEffect的第一個參數能夠return一個方法,這個方法就用於在componentDidUnMount時執行。

custome hooks的使用

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,而後在各組件中靈活複用,提升了代碼的利用率,減小了冗餘代碼。

React hooks給咱們帶來了哪些好處?

  • 更好的代碼複用性。咱們都知道,react的思想就是一個頁面能夠拆成n個組件,而後用但想數據流的方式將全部內容串聯起來。若是咱們的react項目比較大,咱們會發現咱們的類組件每每很龐大也很難複用。在hooks以前,react官方推薦了兩種方式來複用組件,一種是使用renderProps的方式,renderProps就是傳遞一個值爲函數的prop來動態渲染組件,例如:
<Wrapper render={({clickEvent}) => ( 
    <button onClick={clickEvent} /> 
  )}/>
複製代碼

另一種方式是HOC,例如withRouter將react-router 的 history、location、match 三個對象傳入prop。而hooks的custome hooks能夠爲咱們更好的複用組件。

  • 徹底使用函數組件,減小生命週期的誤用,更方便的管理狀態
  • 以前咱們每每猶豫咱們當前組件是否使用函數組件,由於可能考慮後續該組件中會存在狀態,那麼在擁有hooks以後,徹底能夠跳過這方面的考慮了。

總結

React v16.8帶來的hooks必定程度上給咱們提供了巨大的便利,提供了咱們的工做效率,減小了冗餘的代碼。hooks也是一種趨勢,業界也在一直擁抱hooks,所以若是你是react的忠實用戶,勇敢使用hooks吧!

相關文章
相關標籤/搜索