React進階 會使用Hook嗎?

紫棋的一句想要我幫你唱hook嗎?讓hook紅了一把!
作爲一個開發,尤爲是前端開發,在電視上聽到這個詞仍是有點小興奮的(雖然彼hook非此hook)。玩夠這個梗,是否是也要了解一下本身的react hook?html

clipboard.png

1、why hook

使用react有一段時間了,組件化編程也早已成爲習慣。經常使用的兩種編寫組件的方式就是就是class組件函數組件
class組件:經過繼承React.Component來構建組件,雖然提供了state狀態和完備的生命週期函數,可是也有不少不方便的地方。前端

  • 不少事件須要在掛載期componentDidMount更新期componentDidUpdate重複書寫。有些反作用還須要在卸載期componentWillUnmount卸載。代碼重複度很高,並且難以理解,一旦忘記了就會引發很多bug。
componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  • 經過class書寫天然會碰到 this 的問題,要想使用this函數不能忘記綁定。代碼十分冗餘。

函數組件:經過函數直接書寫的組件,看起來是簡潔了許多,可是不能使用state,也沒有生命週期函數、更不能使用react的一些其餘特性。只能經過父組件傳遞進來,任人魚肉,只能慘淡的淪爲展現組件react

那麼,函數組件就只能淪爲展現的花瓶嗎?能結合class組件能使用react特性的優勢和函數組件簡潔優雅的特性嗎?好在react hook來了,函數組件的春天來了。編程

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

2、 what's hook

是否是感受hook挺神奇的?hook是什麼?怎麼使用?不用驚訝,hook就是一個鉤子函數,鉤着react的一些api供給函數組件使用。數組

先看一個官方的useState例子:函數

import React, { useState } from 'react';

function Example() {
  // 聲明一個叫 「count」 的 state 變量。
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

引入一個hook(useState函數),傳入一個初始值0,而後函數返回了一個數組,經過數組的結構賦值,取得state, count = 0和修改count值的函數setCount。當button發生點擊事件時,觸發setCount函數,而且傳入新的count值完成對state值的修改,並展現到頁面上。代碼轉換成class組件以下:組件化

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>
    );
  }
}

看明白了嗎?性能

Hook概念

Hook 是一些可讓你在函數組件裏「鉤入」 React state生命週期等(context、refs)特性的函數。
React 內置了一些像 useState 這樣的 Hook。你也能夠建立你本身的 Hook 來複用不一樣組件之間的狀態邏輯。優化

hook使用規則

一個組件中可使用多個hook,每一個hook會獨立存在,內部狀態不會共用。每次更新組件時,hook會按順序從上到下執行。

function Form() {
  // 1. Use the name state variable
  const [name, setName] = useState('Mary');

  // 2. Use the age state variable
  const [age, setAge] = useState('12');


  // 3. Use the surname state variable
  const [surname, setSurname] = useState('Poppins');

  // ...
}
第一次執行結果:
設置狀態name Mary;
設置狀態age 12;
設置狀態surname Poppins;

第二次執行結果:
設置狀態name Mary;
設置狀態age 12;
設置狀態surname Poppins;

那麼,react是如何知道哪一個 state 對應 哪個useState?很簡單, React 靠的是 Hook 調用的順序。

react hook 使用,要遵循下面兩個規則:

  • 只在最頂層使用 Hook
    react依靠hook調用順序來對應state,因此調用順序不能變。

    function Form() {
      // 1. Use the name state variable
      const [name, setName] = useState('Mary');
    
      // 2. Use the age state variable
      if(!name){
          const [age, setAge] = useState('12');
       }
    
      // 3. Use the surname state variable
      const [surname, setSurname] = useState('Poppins');
    
      // ...
    }
    第一次執行結果:
    設置狀態name Mary;
    設置狀態age 12;
    設置狀態surname Poppins;
    
    第二次執行結果:
    設置狀態name Mary;
    設置狀態age 12;//判斷後會被忽略
    設置狀態surname 12;//此時設置爲12,與需求已不符

    也就是說只能在頂層使用hook。

  • 只在 React 函數中調用 Hook

    在class組件中是沒法使用hook函數的,只能在函數組件中使用。
    在自定義hook中也可使用。

內置Hook

上面使用的useState Hook是一種可讓你在函數組件內使用state的內置hook,除此以外react還提供了許多內置hook:
基礎 Hook

useState
useEffect
useContext

額外的 Hook

useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue

下面看一下 useState、useEffect兩個經常使用的hook,其餘的使用頻率較低,使用方法也都基本一致其餘hook

3、state Hook

咱們想在函數組件里加入state,點擊+,完成數字累加功能:

import React , {useState} from 'react' 


function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    return <>
        {`${count.name}已經${count.age}歲了`}
        <div onClick={()=>{setCount({name:'janny',age:count.age+1})}}>+</div>
    </>
}

export default Item;

從上面代碼能夠看出來,useState hook提供了在函數組件裏使用state的能力。十分簡潔。
首先,經過react引入useState鉤子函數;
接着,在函數組件內調用useState,並傳入state的初始值,能夠是個值,也能夠是對象。useState函數的返回值個數組,這裏經過數組的結構賦值取得count(變量名能夠隨意定義)和修改count的方法setCount(函數名也能夠隨意定義)。
取值state:能夠經過變量count直接取值
修改state:經過給setCount函數傳遞值來修改

是否是很簡單?之後終於能夠在函數組件裏使用state了,擁有控制本身主權的能力了!除了使用state,還有一種場景也是咱們常常碰到的,那就是函數組件沒有生命週期,一些反作用的操做無法完成!彆着急,react又內置了極好的effect hook!

4、effect Hook

當咱們點擊+時,不只想完成數字累加,並且想要在title裏顯示咱們的修改,若是在class組件裏,咱們能夠在componentDidMount生命週期裏經過setState完成,可是函數組件裏無法完成,即便如今有useState Hook也沒有生命週期函數,無法完成!那就在來一個hook吧,引入effect Hook。

useEffect hook不只提供了生命週期,並且useEffect Hook能夠看作componentDidMountcomponentDidUpdatecomponentWillUnmount 這三個函數的組合。也就是在這一個鉤子函數裏能夠完成不少事!

1.無需清除的反作用

import React , {useState,useEffect} from 'react'

function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
       document.title = count.age
    })
    return <>
        {`${count.name}已經${count.age}歲了`}
        <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div>
    </>
}

export default Item;

好好感覺一下!useEffect就像是一個生命週期函數。這個功能在 class 組件中,須要在兩個生命週期函數(componentDidMount、componentDidUpdate)中編寫重複的代碼。在函數useEffect內進行反作用操做就好了。由於每次掛載、更新後useEffect都會執行。

在 React 組件中有兩種常見反作用操做須要清除的和不須要清除的。上面這種就是不須要清除的反作用。

2.須要清除的反作用

假如,咱們以爲只用手指點擊太累了,想弄個計時器幫咱們執行。

let timer = null;

function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
        clearInterval(timer);//清除
        timer = setInterval(()=>{
            console.log(1)
            setCount({name:'tom',age:count.age+1})
        },1000)
       document.title = count.age
    })
    return <>
        {`${count.name}已經${count.age}歲了`}
        <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div>
    </>
}

這樣就很棒,換上自動擋就很舒服!可是當咱們切換路由,移除這個組件後,出去看看風景回來!發現定時器沒有停,本身玩的很嗨...哎呦喂!

clipboard.png

這就是須要清除的反作用,class組件裏咱們能夠經過在componentWillUnmount生命週期函數裏清除,因此這裏咱們也要清除這些須要清除的反作用!

function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
        clearInterval(timer)
        timer = setInterval(()=>{
            console.log(1)
            setCount({name:'tom',age:count.age+1})
        },1000)
       document.title = count.age;
       return function(){//add
        clearInterval(timer)
       }
    })
    return <>
        {`${count.name}已經${count.age}歲了`}
        <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div>
    </>
}

useEffect清除反作用的方式也很簡單,return 一個函數,在函數裏清除就能夠了。

3.優化 useEffect

上面說了useEffect hook在每次掛載、更新後都會執行。這不就是跟setState在性能方面形成的問題同樣嚴重嗎?class組件 經過在 componentDidUpdate 進行優化,useEffect怎麼優化!

能夠經過useEffect函數的第二個參數進行優化。

1.傳遞空數組,表示函數只執行一次!這就告訴 React 你的 effect 不依賴於 props 或 state 中的任何值,因此它永遠都不須要重複執行。effect 內部的 props 和 state 就會一直擁有其初始值。更接近你們熟悉的 componentDidMount 。

useEffect(()=>{
 ...
},[])

2.傳遞包含state的數組

會經過===比較count的前一次渲染的值這一次要渲染的值,若是全等則React 會跳過這個 effect,這就實現了性能的優化。必定要將全部須要比較的state都放進數組,不然將不會進行比較,也就會直接跳過

useEffect(()=>{
 ...
},[count])

5、自定義Hook

react內置了多hook,咱們也能夠對這些hook進行業務方面的封裝,寫出屬於本身的hook!咱們將上面的疊加計時封裝成一個自定義hook:數字每秒疊加,而且修改顯示在title上。

import React , {useState,useEffect} from 'react'

function useTitle(){//自定義hook
    let timer = null;
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
        clearInterval(timer)
        timer = setInterval(()=>{
            setCount({name:'tom',age:count.age+1})
        },1000)
       document.title = count.age;
       return function(){
        clearInterval(timer)
       }
    },[count])
    
    return count;//返回一個對象
}


function Item(){//自定義hook使用
   const count = useTitle()
    return <>
        {`${count.name}已經${count.age}歲了`}
    </>
}

將一些公共代碼提取出來,最後返回須要在頁面顯示的數據就成了

6、總結

到這裏,react hook已經有所瞭解了吧!就是讓函數組件可以像class組件同樣自由自在的使用react的特性的函數。能夠不影響以前任何業務,在項目中徹底可選100%向後兼容的react新特性!快在項目中用起來吧!

若有不妥!歡迎指正
相關文章
相關標籤/搜索