React Hook | 必 學 的 9 個 鉤子

2021-03-02 前端自學社區

React Hook 指南
什麼是 Hook ?❝

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

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

❞Hook 出現解決了什麼 ?❝[ ] 組件之間狀態復用, 例如:使用useContext 可以很好的解決狀態復用問題,或者自定義Hook 來定製符合自己業務場景遇到的狀態管理。[ ] 在函數組件中 生命周期的使用,更好的設計封裝組件。在函數組件中是不能直接使用生命周期的,通過 Hook 很好的解決了此問題。[ ] 函數組件與 class 組件的差異,還要區分兩種組件的使用場景。使用 Hook 完全不用去想這些,它可以使用更多 React 新特性。❞什麼時候使用 Hook ?❝❞React 內置的 Hook❝useImperativeHandle 子組件暴露值/方法useLayoutEffect完成副作用操作,會阻塞瀏覽器繪製❞useState 狀態管理❝

在 class 組件中,我們獲取 state  是 通過 this.state  來獲取的。

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

❞useState 定義  / 使用❝

const [state, setState] = useState(initialState)

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 個生命周期函數:

❞無需清除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對象在組件的整個生命周期內保持不變」

作用:在函數組件中的一個全局變量,不會因為重複 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 來 達到組件數據共享❞什麼是 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注意:依賴對應的值,當對應的值發生變化時,才會重新計算(可以依賴另外一個 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 的參數:

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

小結❝

useMemo 和 useCallback 功能類似,都是提升性能優化。

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

歡迎 讀者 與 我交流。


網上對 useMemo 和 useCallback 的看法 ?❝

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

useMemo 本身名字就是和緩存有關聯的,本質上就為了解決一個事情,在 render 裡面不要直接創建對象或者方法什麼的,因為組件每渲染一次,就會創建一次(比如 style 或者一些常量狀態),造成不必要的資源浪費。理想情況應當是,如果存在依賴,只在依賴變化時重新創建,不存在依賴,那就只創建一次。表面上看,如果所有狀態都用 useMemo,肯定沒什麼問題,但你還需從緩存的代價上來分析這個問題,如果使用 useMemo 緩存一個狀態的代價大於它帶來的優勢,那是不是反而適得其反了?

❞大家對 useMemo 和 useCallback  有何看法,歡迎在下方評論或者加我討論。❞useImperativeHandle定義❝

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

useImperativeHandle作用 :

子組件可以暴露給父組件 實例使用

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




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) => newState 的 reducer,並返回當前的 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>
    )
}

相關焦點

  • 十一個優質React Hook庫, 收藏備用
    它是React鉤子庫(14.8k)中GitHub啟動數量最多的平臺之一。它提供的主要功能:地址:https://github.com/react-hook-form/react-hook-form使用案例:import React from "react";import { useForm } from "react-hook-form";
  • React 16.8 之 React Hook
    有了 react hook 就不用寫class(類)了,組件都可以用function來寫,不用再寫生命周期鉤子,最關鍵的,不用再面對this了!1.只能在最頂層使用hook不要在循環,條件或者嵌套函數中調用hook,確保總是在react函數的最頂層調用,目的是為了確保hook在每一次渲染中都按照同樣的順序被調用,這讓react能夠在多次的useState和useEffect(另一種hook,下面會說)調用之間保持狀態的正確,來保證多個state
  • React Hook 入門教程
    react hook 產生的原因在組件之間復用狀態邏輯很難組件之間復用狀態邏輯很困難,我們可能會把組件連結到我們的 store 裡面。,是學習 react 的一大大門檻,首先你得非常理解 JS 當中的 this 的工作方式,不然你怎麼綁定事件可能都是一個問題。
  • react中關於hook介紹及其使用
    前言最近由於公司的項目開發,就學習了在react關於hook的使用,對其有個基本的認識以及如何在項目中去應用hook。能夠解決你在不使用class組件的情況下去體現react的特性需要注意的一點就是hook和class組件是不能夠同時使用的,在實際的使用過程中一定要注意,否則就會出現報錯那麼接下來所要介紹的部分就是如何去使用hookstate hook
  • React Hook起飛指南
    16.8目前放出來了10個內置hook,但僅僅基於以下兩個API,就能做很多事情。
  • 30 分鐘精通 React 今年最勁爆的新特性 —— React Hooks
    這個函數之所以這麼了不得,就是因為它注入了一個hook--useState,就是這個hook讓我們的函數變成了一個有狀態的函數。除了useState這個hook外,還有很多別的hook,比如useEffect提供了類似於componentDidMount等生命周期鉤子的功能,useContext提供了上下文(context)的功能等等。
  • 30 分鐘精通 React 新特性——React Hooks
    這個函數之所以這麼了不得,就是因為它注入了一個hook-- useState,就是這個hook讓我們的函數變成了一個有狀態的函數。除了 useState這個hook外,還有很多別的hook,比如 useEffect提供了類似於 componentDidMount等生命周期鉤子的功能, useContext提供了上下文(context)的功能等等。
  • 5 分鐘掌握 Python 中的 Hook 鉤子函數
    什麼是Hook經常會聽到鉤子函數(hook function)這個概念,最近在看目標檢測開源框架mmdetection,裡面也出現大量Hook的編程方式,那到底什麼是hook?hook的作用是什麼?what is hook ?鉤子hook,顧名思義,可以理解是一個掛鈎,作用是有需要的時候掛一個東西上去。具體的解釋是:鉤子函數是把我們自己實現的hook函數在某一時刻掛接到目標掛載點上。
  • hook是「鉤子」,那美國人常說的「hook up」是什麼意思呢?
    off the hookhook做名詞解釋是「掛鈎,吊鉤」,做動詞解釋是「鉤住」,off有「離開、脫掉」之意,那off the hook字面意思就是「脫掉了掛鈎,擺脫掉了」,延伸過來,我們就能知道,本意是指「擺脫困境、脫身」。所以「I am off the hook」意思就是「我終於解脫了」。
  • 手寫ReactHook核心原理,再也不怕面試官問我ReactHook原理
    不,還有個問題:就說我們這裡只是用了一個useState,要是我們使用了很多個呢?難道要聲明很多個全局變量嗎?import React ,{useState}from 'react';import ReactDOM from 'react-dom';import '.
  • 程式設計師們之間常說的鉤子「hook」是啥意思?
    程式設計師尤其 Windows 程式設計師會經常說「下個鉤子」,小編還是一名學生的時候,對說出這個短語的人真是崇拜至極。這麼多年過去了,小編還會不經意間蹦出這幾個字,即使已經了解這是一名程式設計師的基本功,說起來依然覺得有範兒。
  • I'll solve the problem by hook or by crook是用鉤子解決問題嗎
    這句話直譯貌似我要用鉤子解決這個問題。其實,正確的翻譯應該是:我無論如何要解決這個問題。hook與crook都有「鉤子」的意思,by hook or by crook字面意思是「用鉤子」,這個習語實際意思是「用種種方法;不擇手段;千方百計;無論如何」。by hook or by crook英語解釋為by any necessary means。看下老外聊天時怎麼用。
  • 程式設計師常說的鉤子(Hook)是啥意思?
    程式設計師尤其Windows程式設計師會經常說「下個鉤子」,當年果果還是一名學生的時候,word天吶,對說了這個詞語的人真是崇拜至極,心裡默念著牛逼。
  • 詳細 preact hook 源碼逐行解析
    源碼分析,理解和掌握react/preact的hook用法以及一些常見的問題。雖然react和preact的實現上有一定的差異,但是對於hook的表現來說,是基本一致的。對於 preact的hook`分析,我們很容易就記住 hook 的使用和防止踩一些誤區。preact hook 作為一個單獨的包preact/hook引入的,它的總代碼包含注釋區區 300 行。
  • 你可能不知道的 React Hooks
    應提供三個控制按鈕: 啟動、停止和清除。Hooks API Reference[9]: Cleaning up an effect[10].用好 React Hooks 的清單服從Rules of Hooks 鉤子的規則[26].Prefer 更喜歡useReducer or functional updates for 或功能更新useStateto prevent reading and writing same value in a hook.
  • 鉤子:by hook or by crook
    「By hook or by crook」 例句:   One thing you can say about Jack is that he always gets the job done, whether by hook or by crook.
  • React:useHooks小竅門
    但是在此之前,請確保你已經看了hook的官方文檔useEventListener如果你發現自己使用useEffect添加了許多事件監聽,那你可能需要考慮將這些邏輯封裝成一個通用的hook。在下面的使用竅門裡,我們創建了一個叫useEventListener的hook,這個hook會檢查addEventListener是否被支持、添加事件監聽並且在cleanup鉤子中清空監聽。
  • 有關鉤子:by hook or by crook
    「By hook or by crook」 例句:   One thing you can say about Jack is that he always gets the job done, whether by hook or by crook.
  • 英語口語表達:off the hook
    學英語是個循序漸進的過程,每天掌握好一個英語口語表達,利用閒暇時間日積月累學英語。今天的英語口語表達是:off the hook.hook是「鉤子」的意思,off the hook 的字面意思是「脫離了鉤子」,據說這個口語表達最初由來是:上了鉤的魚又擺脫了魚鉤,重新獲得了自由。現在一般用來形容人,表示:「脫離困境,擺脫麻煩,如釋重負,免於責罰」。
  • ​如何成為一個更好的 React 開發者?
    從那一天起,我開始把我學到的東西納入到我的工作流程中。相信我,這有助於我理解React,並且讓我的代碼看上來不像新手寫出來的。閒話不再贅述。接下來我將分享一些我學到的技巧,這些技巧有些是從我的個人研究中得到的,也有些是通過項目和經驗學到的。註:下面技巧的重要性不以先後次序決定。