React 初探之 Hook

概述

Hook 使你在非 class 的情況下可以使用更多的 React 特性。Hook 是一些可以在函數(shù)組件里鉤入React state 及生命周期等特性的函數(shù)。Hook 就是 JavaScript 函數(shù),但是使用它們會(huì)有兩個(gè)額外的規(guī)則

  • 只在最頂層使用 Hook。不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook。
  • 只在 React 函數(shù)中調(diào)用 Hook。不要在普通的 JavaScript 函數(shù)中調(diào)用 Hook??梢栽?
    1. React 的函數(shù)組件中調(diào)用 Hook
    2. 在自定義 Hook 中調(diào)用其他 Hook

Hook 是一種服用狀態(tài)邏輯的方式,它不復(fù)用 state 本身。事實(shí)上 Hook 的每次調(diào)用都有一個(gè)完全獨(dú)立的 state,因此可以在單個(gè)組件中多次調(diào)用同一個(gè)自定義 Hook。

React 依據(jù) Hook 調(diào)用的順序知道哪個(gè) state 對(duì)應(yīng)哪個(gè) useState

State Hook

在 React 函數(shù)組件上添加內(nèi)部 state

Effect Hook

默認(rèn)情況下,React 會(huì)在每次渲染后調(diào)用副作用函數(shù),包括第一次渲染的時(shí)候。通過(guò)使用 Hook,可以把組件內(nèi)相關(guān)的副作用組織在一起(例如創(chuàng)建訂閱及取消訂閱),而不是要把他們拆分到不同的生命周期函數(shù)里。

  • 副作用 => 數(shù)據(jù)獲取,設(shè)置訂閱以及手動(dòng)更改 React 組件中的 DOM 都屬于副作用

在 React 組件中有兩種常見(jiàn)副作用操作:需要清除的和不需要清除的

無(wú)需清除的 effect

在 React 更新 DOM 之后運(yùn)行一些額外的代碼。發(fā)送網(wǎng)絡(luò)請(qǐng)求,手動(dòng)變更 DOM,記錄日志,這些都是常見(jiàn)的無(wú)需清除的操作。React 保證了每次運(yùn)行 effect 的同時(shí),DOM 都已經(jīng)更新完畢。

每次重新渲染,都會(huì)生成新的 effect,替換掉之前的。effect 更像是渲染結(jié)果的一部分,每個(gè) effect 屬于一次特定的渲染

需要清除的 effect

訂閱外部數(shù)據(jù)源這種副作用是需要清除的,可以防止引起內(nèi)存泄漏。副作用函數(shù)可以通過(guò)返回一個(gè)函數(shù)來(lái)指定如何清除副作用

React 何時(shí)清除 effect?React 會(huì)在組件卸載的時(shí)候執(zhí)行清除操作 + effect 在每次渲染的時(shí)候都會(huì)執(zhí)行 => React 會(huì)在執(zhí)行當(dāng)前 effect 之前對(duì)上一個(gè) effect 進(jìn)行清除。

自定義 Hook

自定義 Hook 可以在不增加組件的情況下在組件之間重用一些狀態(tài)邏輯,即可以將組件邏輯提取到可重用的函數(shù)中。自定義 Hook 更像是一種約定而不是功能。

  • 自定義 Hook 必須以 use 開(kāi)頭。
  • 在兩個(gè)組件中使用相同 Hook 不會(huì)共享 state。每次使用自定義 Hook 時(shí),其中所有 state 和副作用都是完全隔離的。
  • 每次調(diào)用 Hook,自定義 Hook 都會(huì)獲取獨(dú)立的 state。直接調(diào)用自定義 Hook 時(shí),從 React Dee角度來(lái)看,組件只是調(diào)用了 useStateuseEffect

常用 Hook

useState

const [state, setState] = useState(initialState);

setState

setState 可以接收

  • 新值,從而更新 state,使得 state 為新值
  • 函數(shù),參數(shù)為先前的 state,state 為函數(shù)的返回值
    const [count, setCounnt] = useState(0);
    setCounnt(prevCount => prevCount + 1);
    

useState

  • 如果初始 state 需要通過(guò)復(fù)雜計(jì)算獲得,則可以傳入一個(gè)函數(shù),在函數(shù)中計(jì)算并返回初始的 state,此函數(shù)只在初始渲染時(shí)被調(diào)用
    const [state, setState] = useState(() => {
        const initState = someExpensiveComputation(props);
        return initState;
    });
    

useEffect

useEffect 可以看做 componentDidMount,componentDidUpdatecomponentWillUnmount 這三個(gè)函數(shù)的組合。

通過(guò)傳遞數(shù)組作為 useEffect 的第二個(gè)可選參數(shù)從而跳過(guò) Effect 進(jìn)行性能優(yōu)化。數(shù)組中包含了所有外部作用域中會(huì)隨時(shí)間變化并且在 effect 中使用的變量。

如果想執(zhí)行只運(yùn)行一次的 effect(僅在組件掛載和卸載時(shí)執(zhí)行),可以傳遞一個(gè)空數(shù)組([])作為第二個(gè)參數(shù)。

useEffect 的 effect 的執(zhí)行時(shí)機(jī)

componentDidMountcomponentDidUpdate 不同的是,在瀏覽器完成布局與繪制之后,傳給 useEffect 的函數(shù)會(huì)延遲調(diào)用,所以不應(yīng)在函數(shù)中執(zhí)行阻塞瀏覽器更新屏幕的操作

useRef

const redContainer = useRef(initValue);

useRef 返回一個(gè)可變的 ref 對(duì)象,其 .current 屬性被初始化為傳入的參數(shù)(initValue)。返回的 ref 對(duì)象在組件的整個(gè)生命周期內(nèi)保持不變。

本質(zhì)上,useRef 就像是可以在其 .current 屬性中保存一個(gè)可變值的"盒子"。

useRef() Hook 不僅可以用于 DOM refs,「ref」對(duì)象是一個(gè) current 屬性可變且可以容納任意值的通用容器。

例1

function TextInputWithFocusButton() {
    const inputEle = useRef(null);
    const onButtonClick = () => {
        // current 指向已掛載到 DOM 上的文本輸入元素
        inputEle.current.focus();
    }
    return (
        <>
            <input ref={inputEle} type='text' />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    )
}

例2:useRef 可以很方便的保存任何可變值

function Timer() {
    const intervalRef = useRef();
    useEffect(() => {
        intervalRef.current = setinterval(() => {
            // ...
        });
        
        return () => {
            clearInterval(intervalRef.current);
        }
    });
}

注: 當(dāng) ref 對(duì)象內(nèi)容發(fā)生變化時(shí),useRef 并不會(huì)通知。變更 .current 屬性不會(huì)引起組件重新渲染。如果想要在 React 綁定和解綁 DOM 節(jié)點(diǎn)的 ref 時(shí)運(yùn)行某些代碼,則需要使用 useCallback 來(lái)實(shí)現(xiàn)

useCallback

返回一個(gè) memoized 函數(shù)。

const memoizedCallback = useCallback(() => {
    doSomething(a, b);
}, [a, b]);

把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項(xiàng)數(shù)組作為參數(shù)傳入 useCallback,它將返回該回調(diào)函數(shù)的 memoized 版本,該回調(diào)函數(shù)僅在某個(gè)依賴項(xiàng)改變時(shí)才會(huì)更新。當(dāng)把回調(diào)函數(shù)傳遞給經(jīng)過(guò)優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子組件時(shí),它將非常有用。

useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps)

useMemo

返回一個(gè) memoized 值

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

把創(chuàng)建函數(shù)和依賴項(xiàng)數(shù)組作為參數(shù)傳入 useMemo,它僅會(huì)在某個(gè)依賴項(xiàng)改變時(shí)才重新計(jì)算 memoized 值。這種優(yōu)化有助于避免在每次渲染時(shí)都進(jìn)行高開(kāi)銷(xiāo)的計(jì)算。

如果沒(méi)有提供依賴項(xiàng),useMemo 在每次渲染時(shí)都會(huì)計(jì)算新的值。可以把 useMemo 作為性能優(yōu)化的手段,但不要把它當(dāng)成語(yǔ)義上的保證

注:傳入 useMemo 的函數(shù)會(huì)在渲染期間執(zhí)行。不要在這個(gè)函數(shù)內(nèi)部執(zhí)行與渲染無(wú)關(guān)的操作,諸如副作用這類的操作屬于 useEffect 的適用范疇

注意:

  • useState 不會(huì)自動(dòng)合并并更新對(duì)象??墒鞘褂煤瘮?shù)式的 setState 達(dá)到合并并更新對(duì)象的效果
    setState(prevState => ({...prevState, ...updateValues}));
    
  • 如果在渲染期間執(zhí)行了高開(kāi)銷(xiāo)的計(jì)算,則可以使用 useMemo 來(lái)進(jìn)行了優(yōu)化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 自從Hooks出現(xiàn),函數(shù)式組件(Function Component)的功能在不斷豐富,你很可能已經(jīng)運(yùn)用Hooks...
    tracyXia閱讀 11,590評(píng)論 2 7
  • 主要介紹 useState useEffect useReducer useContext 用法 你還在為...
    叫我蘇軾好嗎閱讀 27,653評(píng)論 3 41
  • Hook是React16.8的新增特性。它可以讓你在不編寫(xiě)class的情況下使用state以及其他的react特性...
    逝去丶淺秋閱讀 665評(píng)論 0 2
  • Hooks是 React v16.8 的新特性,可以在不使用類組件的情況下,使用 state 以及其他的React...
    hellomyshadow閱讀 13,703評(píng)論 0 5
  • 這是一則隨心到信馬由韁的動(dòng)態(tài)。 我喜歡有事沒(méi)事追憶過(guò)往?;蚴欠掌?、或是從頭到尾看一遍寫(xiě)過(guò)的文字、或是聽(tīng)聽(tīng)那些年...
    婠執(zhí)閱讀 524評(píng)論 1 8

友情鏈接更多精彩內(nèi)容