React Hook | 必 學 的 9 個 鉤子

關注公衆號,更多精彩內容等着你
前端



React Hook 指南

什麼是 Hook ?

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

Hook 本質上就是一個函數,它簡潔了組件,有本身的狀態管理,生命週期管理,狀態共享。web

  • useState
  • useEffect
  • useContext
  • useReducer

Hook 出現解決了什麼 ?

  • [ ] 組件之間狀態複用, 例如:使用useContext 能夠很好的解決狀態複用問題,或者自定義 Hook 來定製符合本身業務場景遇到的狀態管理。
  • [ ] 在函數組件中 生命週期的使用,更好的設計封裝組件。在函數組件中是不能直接使用生命週期的,經過 Hook 很好的解決了此問題。
  • [ ] 函數組件與 class 組件的差別,還要區分兩種組件的使用場景。使用 Hook 徹底不用去想這些,它可使用更多 React 新特性。

何時使用 Hook ?

  1. 在函數組件頂層調用

  2. 在 函數中使用  /  自定義 Hook 中使用

React 內置的 Hook


    1. useState   狀態管理

    1. useEffect  生命週期管理

    1. useContext 共享狀態數據

    1. useMemo   緩存值

    1. useRef  獲取Dom 操做

    1. useCallback  緩存函數

    1. useReducer    redux 類似

    1. useImperativeHandle 子組件暴露值/方法

    1. useLayoutEffect完成反作用操做,會阻塞瀏覽器繪製

useState 狀態管理

class 組件中,咱們獲取 state  是 經過 this.state  來獲取的。面試

而在函數組件中, 是沒有 this 的, 咱們可使用 Hook 提供的 useState 來管理和維護 state .redux

useState 定義  / 使用

const [state, setState] = useState(initialState)數組

  • setState 爲更新 satate 方法
  • useState(initialState) initialState 爲初始值

完整栗子

import {useState} from 'react';

export default () => {
    const [data, setData] = useState('微信公衆號: 前端自學社區')
    return (
        <div>
            <h1>{data}</h1>
            {/* 更新 state */}
            <button onClick={()=>{setData('微信公衆號: 前端自學社區  666')}}></button>
        </div>

    )
}

useEffect 生命週期管理

定義

useEffect 能夠看做是 函數式 組件 的 生命週期管理。瀏覽器

由於在 函數式組件中沒法直接使用生命週期,就必須託管 Hook 來進行管理使用了。緩存

useEffect 可使用的 3 個生命週期函數:性能優化

  • componentDidmount
  • componentDidUpdate
  • componentWillUnmount

無需清除Effect 使用

什麼是無需清除 Effect 使用?

「React 更新 DOM 以後運行一些額外的代碼」微信

那麼它就是在生命週期的compoentDidmount  和 componentUpdate 中執行便可。

    useEffect(() => {
        //默認會執行  
        // 這塊至關於 class 組件 生命週期的
        //compoentDidmount    compoentDidUpdate
    }, [])

清除Effect 使用

1. 什麼是 清除Effect  ?

當組件進行卸載時,須要執行某些事件處理時,就須要用到 class 組件生命週期的 componentUnmount .

useEffect 中很方便使用,在內部返回一個方法便可,在方法中寫相應業務邏輯

2. 爲何 要在 Effect  中返回一個函數 ?

這是 effect 可選的清除機制。每一個 effect 均可以返回一個清除函數。如此能夠將添加和移除訂閱的邏輯放在一塊兒。它們都屬於 effect 的一部分。

    useEffect(()=>{
        return () => {
            console.log('組件卸載時執行')
        }
    })

監聽 state 變化

能夠經過控制 監聽 state 變化來實現相應的業務邏輯。

    useEffect(() => {
        // 監聽num,count  狀態變化
        // 不監聽時爲空 [] , 或者不寫
    }, [num, count])

完整栗子

import { useState, useEffect } from 'react';

export default () => {
    const [num, setNum] = useState(0)
    const [count, setCount] = useState(1)

    useEffect(() => {
        //默認會執行  
        // 這塊至關於 class 組件 生命週期的 compoentDidmount compoentDidUpdate
        console.log(`num: ${num}`)
        console.log(`count: ${count}`)

        // 組件在卸載時,將會執行 return 中內容
        return () => {
            // 至關於 class 組件生命週期的 componentWillUnMount 
            console.log('測試')
        }
    }, [num])

    return (
        <div>
            <h1>{num}</h1>
            <button onClick={() => { setNum(num + 1) }}> 更新Num</button>
            <hr />
            <h1>{count}</h1>
            <button onClick={() => { setCount(count + 1) }}> 更新Count</button>
        </div>

    )
}

useRef

什麼是 useRef ?

useRef 返回的是一個可變的ref對象,它的屬性current被初始化爲傳入的參數(initialValue),「返回的ref對象在組件的整個生命週期內保持不變」

做用:

  1. 獲取Dom操做,例如 獲取 input 焦點

  2. 獲取子組件的實例(只有類組件可用)

  3. 在函數組件中的一個全局變量,不會由於重複 render 重複申明

栗子

import {useRef} from 'react';


export default () => {
    const inputRef = useRef({value:0})
    return (
        <div>
            <h1>測試</h1>
            <input type="text" ref={inputRef} />
            <button onClick={()=>{console.log(inputRef.current.value)}}>獲取input 值</button>
            <button onClick={()=>{inputRef.current.focus()}}>獲取input 焦點</button>
        </div>

    )
}

useContext 狀態數據共享

Context 解決了什麼

在平常開發中,咱們父子組件都是經過props  來進行通訊,若是遇到跨級組件通訊 那麼咱們就很差經過 props 來處理了。

這時候能夠想一想怎麼能夠把 組件 狀態 共享出去使用?

  • Context
  • Redux
  • .....

本小節經過 Context 來 達到組件數據共享

什麼是 Context

數據共享,任何組件均可訪問Context 數據。

React 中,組件數據經過 prop  來達到 自上而下的傳遞數據,要想實現全局傳遞數據,那麼可使用 Context .

注意:

Context 主要應用場景在於不少不一樣層級的組件須要訪問一樣一些的數據。請謹慎使用,由於這會使得組件的複用性變差。

建立 Context

在使用Context 前提,必須建立它,能夠爲它單首創建一個文件來管理Context,

import React from 'react';

export const MyContext = React.createContext();

使用 Context

在使用Context 時,它一般用在頂級組件(父組件上),它包裹的內部組件均可以享受到state 的使用和修改。

經過Context.Provider 來進行包裹,值經過value = {} 傳遞。

子組件如何使用 Context 傳遞過來的值 ?

  • 經過 useContext()  Hook  能夠很方便的拿到對應的值.
// Context.js
import React from 'react';

export const MyContext = React.createContext();
import { useContext } from 'react';
import {MyContext} from '../Context/index'

const result = {
    code:200,
    title:'添加數據成功'
}
const Son = () => {
    const res = useContext(MyContext)
    return (
        <>
            <div>
                <h1>{res.code}</h1>
                <hr/>
                <h2>{res.title}</h2>
            </div>
        </>

    )
}


export default  () => {
    return (
        <MyContext.Provider value={result}>
            <div>
                <h1>前端自學社區</h1>
                <Son/>
            </div>
        </MyContext.Provider>

    )
}


useMemo 提高性能優化

定義

useMemo用於性能優化,經過記憶值來避免在每一個渲染上執⾏高開銷的計算。

useMemo 參數:

  • useMemo 返回值是 memoized 值,具備緩存做用
  • array控制 useMemo從新執⾏的數組, array 中 的 state 改變時纔會 從新執行 useMemo

注意:


    1. 不傳數組,每次更新都會從新計算

    1. 空數組,只會計算一次

    1. 依賴對應的值,當對應的值發生變化時,纔會從新計算(能夠依賴另一個 useMemo 返回的值)

栗子


import { useState, useMemo} from 'react';


export default () => {
    const  [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const newValue = useMemo(()=>{
        console.log(`count 值爲${count}`)
        console.log(`num 值爲 ${num}`)
        return count+num
    },[count])
    return(
        <div>
            <h1>{count}</h1> 
            <button onClick={()=>{setCount(count+1)}}>count + 1</button>
            <hr/>
            <h1>{num}</h1> 
            <button onClick={()=>{setNum(num+1)}}>Num + 1</button>
            <hr/>
            <h2>{newValue}</h2>
        </div>

    )
}

解析栗子

當點擊了 5 次更新 num 值,頁面中 newValue 的值始終顯示爲 0,這是爲何呢?

由於我在 useMemo 監聽記錄的是 count 的值,當 count 值發生變化時,頁面上的newValue 在會從新計算,雖然你點擊了 5 次 更新 num ,頁面沒有更新,可是已經緩存起來了,當點擊 更新 count 時,它會  計算 count+1 的值 和 num 緩存的值 , 最終結果 爲 5。

減小了計算消耗。

useCallback 提高性能優化

定義

useCallback 能夠說是 useMemo 的語法糖,能用 useCallback 實現的,均可以使用 useMemo, 經常使用於react的性能優化。

useCallback 的參數:

  • callback是一個函數用於處理邏輯
  • array  控制 useCallback從新執⾏的數組, array改變時纔會從新執⾏ useCallback

使用

它的使用和useMemo 是同樣的,只是useCallback 返回的函數。

import { useState, useCallback} from 'react';


export default () => {
    const  [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const newValue = useCallback(()=>{
        console.log(`count 值爲${count}`)
        console.log(`num 值爲 ${num}`)
        return count+num
    },[count])
    return(
        <div>
            <h1>{count}</h1> 
            <button onClick={()=>{setCount(count+1)}}>count + 1</button>
            <hr/>
            <h1>{num}</h1> 
            <button onClick={()=>{setNum(num+1)}}>Num + 1</button>
            <hr/>
            {/* 調用useCallback 返回的值 */}
            <h2>{newValue()}</h2>
        </div>

    )
}

小結

useMemouseCallback 功能相似,都是提高性能優化。

該採用哪一種方式來最佳實踐,還有待探索。

歡迎 讀者 與 我交流。


網上對 useMemouseCallback 的見解 ?

useCallback  若是在函數式組件中的話,確實應該看成最佳實踐來用,可是使用它的目的除了要緩存依賴未改變的回調函數以外(與 useMemo 相似),還有一點是爲了可以在依賴發生變動時,可以確保回調函數始終是最新的實例,從而不會引起一些意料以外的問題,我感受後者纔是使用 useCallback 的出發點,而非緩存。由於你想啊,即便不用 useCallback,假設這個回調函數也沒有任何依賴狀態,我直接把這個函數聲明在組件外部不也能夠嗎?我直接使用 ref 不是更自由嗎?

useMemo 自己名字就是和緩存有關聯的,本質上就爲了解決一個事情,在 render 裏面不要直接建立對象或者方法什麼的,由於組件每渲染一次,就會建立一次(好比 style 或者一些常量狀態),形成沒必要要的資源浪費。理想狀況應當是,若是存在依賴,只在依賴變化時從新建立,不存在依賴,那就只建立一次。表面上看,若是全部狀態都用 useMemo,確定沒什麼問題,但你還需從緩存的代價上來分析這個問題,若是使用 useMemo 緩存一個狀態的代價大於它帶來的優點,那是否是反而拔苗助長了?

你們對 useMemouseCallback  有何見解,歡迎在下方評論或者加我討論。

useImperativeHandle

定義

useImperativeHandle 可讓你在使用 ref 時自定義暴露給父組件的實例值。在大多數狀況下,應當避免使用 ref 這樣的命令式代碼。useImperativeHandle 應當與 forwardRef 一塊兒使用。

useImperativeHandle做用 :

子組件能夠暴露給父組件 實例使用

格式: useImperativeHandle(ref,()=>{},[])

  • 參數1:子組件向父組件暴露的實例

  • 參數2:函數,傳遞的父組件可操做的實例和方法

  • 參數3:監聽狀態,更新狀態




import {useState,useImperativeHandle, forwardRef,useRef} from 'react';


const Son = forwardRef( (props,ref) => {
    const inputRef = useRef(0)
    const domRef = useRef()
    const [state, setState] = useState('等待')
    useImperativeHandle(ref,()=>({
        focus:() => {inputRef.current.focus()},
        domRef
    }))
    return (
        <div>
            <h1>{state}</h1>
            <hr/>
            <input type="text" ref={inputRef}/>
            <h2  ref={domRef}>測試---------useImperativeHandle</h2>
        </div>

    )
})


export default () => {
    const refFather = useRef(0)
    return (
        <div>
            <h1>父組件</h1>
            <Son ref={refFather} />
            <button onClick={()=>{refFather.current.focus()}}>獲取子組件實例------獲取input焦點</button>
            <button onClick={()=>{console.log(refFather.current.domRef.current.innerHTML)}}>獲取子組件實例------獲取h2 Dom</button>
        </div>

    )
}

useReducer

定義

它是 useState 的替代方案。它接收一個形如 (state, action) => newStatereducer,並返回當前的 state以及與其配套的 dispatch 方法。

若是熟悉Redux 使用的話,用useReducer 就是輕車熟路了,發車了。

使用Reducer實現一個加減器


import {useReducer} from 'react';


export default () => {
    const [state, dispatch] = useReducer((state,action)=> {
        switch (action.type){
            case 'addNum':
                return {
                    num:state.num+1
                }
            case 'subtractNum':
                return {
                    num:state.num-1
                }
        }
            
    },{
        num:0
    })
    return (
        <div>
            <h2>{state.num}</h2>
            <button onClick={()=>{dispatch({type:'addNum'})}}> 增長num</button>
            <button onClick={()=>{dispatch({type:'subtractNum'})}}> 增長num</button>
        </div>

    )
}






2020前端技術面試必備Vue:(一)基礎快速學習篇

2020前端技術面試必備Vue:(二)Router篇

2020前端技術面試必備Vue:(二)組件篇

2020前端技術面試必備Vue:(四)Vuex狀態管理

Vue權限路由思考

Vue 組件通訊的 8 種方式

MYSQL經常使用操做指令

TypeScript學習指南(有PDF小書+思惟導圖)





結語

❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更好的文章

關注公衆號 「前端自學社區」,便可獲取更多前端高質量文章!

關注後回覆關鍵詞「加羣」, 便可加入 「前端自學交流羣」,共同窗習進步。

關注後添加我微信拉你進技術交流羣




本文分享自微信公衆號 - 前端自學社區(gh_ce69e7dba7b5)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索