1、什么是hook?
react hook是react 16.8推出的方法,能夠讓函數(shù)式組件像類式組件一樣擁有state、ref、生命周期等屬性。
2、為什么要出現(xiàn)hook?
函數(shù)式組件是全局當(dāng)中一個(gè)普通函數(shù),在非嚴(yán)格模式下this指向window,但是react內(nèi)部開(kāi)啟了嚴(yán)格模式,此時(shí)this指向undefined,無(wú)法像類式組件一樣使用state、ref,函數(shù)式組件定義的變量都是局部的,當(dāng)組件進(jìn)行更新時(shí)會(huì)重新定義,也無(wú)法存儲(chǔ),所以在hook出現(xiàn)之前,函數(shù)式組件有很大的局限性,通常情況下都會(huì)使用類式組件來(lái)進(jìn)行代碼的編寫。
3、有哪些常用的hook?
(1) useState
使函數(shù)式組件也能保存狀態(tài)的一個(gè)hook,這個(gè)hook的入?yún)⑹菭顟B(tài)的初始值,返回值是一個(gè)數(shù)組,數(shù)組里第一個(gè)參數(shù)為狀態(tài)的值,第二個(gè)參數(shù)為修改狀態(tài)的方法。
1
2
3
4
|
// 初始化 const [ count, setCount ] = useState(0) // 更新 setCount(count+1) |
(2) useEffect
函數(shù)式組件用來(lái)模擬生命周期的hook,可以模擬組件掛載完成、更新完成、即將卸載三個(gè)階段,即componentDidMount、componentDidUpdate、componentWillUnmount。
useEffect的一個(gè)參數(shù)為函數(shù),表示組件掛載、更新時(shí)執(zhí)行的內(nèi)容,在函數(shù)里再返回一個(gè)函數(shù),表示組件即將卸載時(shí)調(diào)用的函數(shù)。
第二個(gè)參數(shù)為可選項(xiàng),可傳入數(shù)組,數(shù)組里可以為空,表示不依賴任何狀態(tài)的變化,即只在組件即將掛載時(shí)執(zhí)行,后續(xù)任何狀態(tài)發(fā)生了變化,都不調(diào)用此hook。數(shù)組里也可以定義一或多個(gè)狀態(tài),表示每次該狀態(tài)變化時(shí),都會(huì)執(zhí)行此hook。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
useEffect(()=>{ // 這樣模擬的是 componentDidMount }, []) useEffect(()=>{ // 這樣模擬的是componentDidMount 以及當(dāng)count發(fā)生變化時(shí)執(zhí)行componentDidUpdate }, [count]) useEffect(()=>{ return ()=>{ // 這樣模擬的是 componentWillUnmount } }, []) |
(3) useContext
在沒(méi)有hook之前,我們通常都會(huì)通過(guò) xxxContext.Provider 和 xxxContext.Consumer 的方式來(lái)傳遞和獲取context的值,使用hook之后,傳遞context的方式不變,但子元素獲取context的方式變得更加的簡(jiǎn)潔。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 以前的定義方式 const CountContext = React.createContext() <CountContext.Provider value={{ count: 10 }}> <...自定義的組件> </CountContext.Provider> // 子元素 <CountContext.Consumer> { value => { console.log(value.count) }} //10 </CountContext.Consumer> //使用context的獲取方式 const countObj = useContext(CountContext) console.log(countObj.count) // 10 |
(4) useRef
useRef和類式組件中createRef用法比較類似,返回一個(gè)ref對(duì)象,這個(gè)對(duì)象在函數(shù)的整個(gè)生命周期都不變,根據(jù)這個(gè)特性,有兩種比較常見(jiàn)的用法。
① 用于dom元素或者組件上,通過(guò)current屬性可以獲取到dom元素或者類式組件的實(shí)例對(duì)象。需要注意的是,無(wú)論是useRef還是createRef或者是回調(diào)形式、字符串形式的ref,都是不能直接給函數(shù)式組件定義的,因?yàn)楹瘮?shù)式組件的this指向undefined,沒(méi)有實(shí)例對(duì)象,只能通過(guò)forwardRef定義到函數(shù)式組件中的某個(gè)dom元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 這樣就將傳遞給函數(shù)式組件的ref綁定在了函數(shù)式組件內(nèi)部的input標(biāo)簽上 import React, { useRef, forwardRef } from 'react' // 使用函數(shù)表達(dá)式的方式定義了一個(gè)函數(shù)式組件 const InputCom = forwardRef((props, ref) => { return <input ref={ref}/> }) export default function refDemo(){ const comRef = useRef() return (<div> <InputCom ref={comRef}/> </div>) } |
② 保存一個(gè)數(shù)據(jù),該數(shù)據(jù)如果不手動(dòng)修改,它在整個(gè)生命周期中都不變
1
2
3
4
5
6
|
const [ count, setCount ] = useState(0) const prevCount = useState(count) // 當(dāng)count發(fā)生變化時(shí),組件更新,對(duì)count的前一次數(shù)據(jù)進(jìn)行保存 useEffect(()=>{ prevCount.current = count }, [count]) |
(5) useReducer
useReducer相當(dāng)于是useState的升級(jí)版,作用與useState類似,都是用來(lái)保存狀態(tài),但它的不同點(diǎn)在于可以定義一個(gè)reducer的純函數(shù),來(lái)處理復(fù)雜數(shù)據(jù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 定義一個(gè)處理數(shù)據(jù)的reducer純函數(shù) function reducer(prevState, action){ switch (action.type){ case 'increment' : return {...prevState, count: prevState.count + 1 } case 'decrement' : return {...prevState, count: prevState.count - 1 } default : return prevState } } // 初始化狀態(tài) const [ count, dispatch ] = useReducer(reducer, { count: 0 }) // 修改狀態(tài),此時(shí)的修改需要派發(fā)一個(gè)action,讓傳入的reducer函數(shù)進(jìn)行處理 dispatch({ type: 'increment' }) |
(6) useCallback
函數(shù)式組件中,每一次更新?tīng)顟B(tài),自定義的函數(shù)都要進(jìn)行重新的聲明和定義,如果函數(shù)作為props傳遞給子組件,會(huì)造成子組件不必要的重新渲染,有時(shí)候子組件并沒(méi)有使用到父組件發(fā)生變化的狀態(tài),此時(shí)可以使用useCallback來(lái)進(jìn)行性能優(yōu)化,它會(huì)為函數(shù)返回一個(gè)記憶的值,如果依賴的狀態(tài)沒(méi)有發(fā)生變化,那么則不會(huì)重新創(chuàng)建該函數(shù),也就不會(huì)造成子組件不必要的重新渲染。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import React, { useState, useCallback, memo } from 'react' const AddBtn = memo((props)=>{ // 使用函數(shù)表達(dá)式的方式定義了一個(gè)函數(shù)式組件 return <button onClick={props.increment}>+1</button> }) export default function CallBackPerformance(){ const [ count, setCount ] = useState(0) const [ show, setShow ] = useState( true ) const increment1 = () => { console.log( 'increment1被調(diào)用了' ) setCount(count+1) } const increment2 = useCallback(()=>{ // 使用了useCallback來(lái)優(yōu)化的函數(shù) console.log( 'increment2被調(diào)用了' ) setCount(count+1) },[count]) return (<div> <div>當(dāng)前計(jì)數(shù):{count}</div> <AddBtn increment={increment1} name= "1" /> <AddBtn increment={increment2} name= "2" /> <button onClick={e => setShow(!show)}>切換show</button> </div>) } // 當(dāng)show這個(gè)狀態(tài)發(fā)生變化時(shí),子組件increment1會(huì)重新渲染,increment2不會(huì)重新渲染 |
(7) useMemo
useMemo也是返回一個(gè)記憶的值,如果依賴的內(nèi)容沒(méi)有發(fā)生改變的話,這個(gè)值也不會(huì)發(fā)生變化,useMemo與useCallback的不同點(diǎn)在于useMemo需要在傳入的函數(shù)里需要return 一個(gè)值,這個(gè)值可以是對(duì)象、函數(shù),格式如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
useMemo(()=>{ return { count } }, [count]) // 使用useCallback時(shí) const increment2 = useCallback(()=>{ setCount(count+1) },[count]) // 使用useMemo模擬useCallback const increment2 = useCallback(()=>{ return ()=>{ setCount(count+1) } },[count]) // useMemo的應(yīng)用場(chǎng)景,當(dāng)要進(jìn)行一些復(fù)雜的計(jì)算時(shí), //計(jì)算的值沒(méi)有發(fā)生變化,并不需要每一次更新都重新計(jì)算 import React, { useState, useMemo } from 'react' const calculateNum = (count) => { console.log( 'total重新計(jì)算了' ) let total = 0 for (let i = 0; i <= count; i++){ total += i } return total } export default function ComplexUseMemo(){ const [ count, setCount ] = useState(10) const [ show, setShow ] = useState( true ) const total = useMemo(()=>{ return calculateNum(count) }, [count]) return (<div> <div>{total}</div> <button onClick={e=>setCount(count+1)}>+1</button> <button onClick={e=>setShow(!show)}>切換show</button> </div>) } |
(8) useImperativeHandle
這個(gè)是與forwardRef配合來(lái)使用的,當(dāng)我們對(duì)函數(shù)式組件使用forwardRef將ref指定了dom元素之后,那就父組件就可以任意的操作指定的dom元素,使用useImperativeHandle就是為了控制這樣的一種行為,指定父元素可操作的子元素的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import React, { useRef, useImperativeHandle, forwardRef } from 'react' const InputComp = forwardRef((props, ref)=>{ const childInputRef = useRef() useImperativeHandle(ref, ()=>({ focus: ()=>{ childInputRef.current.focus() } }), [childInputRef.current]) return <input ref={childInputRef}></input> }) export default function ImperativeHookDemo() { const inputRef = useRef() return (<div> <InputComp ref={inputRef}/> <button onClick={e=>inputRef.current.focus()}>聚焦</button> </div>) } |
(9) useLayoutEffect
這個(gè)方法與useEffect類似,只是執(zhí)行的順序稍有不同,useEffect是在組件渲染繪制到屏幕上之后,useLayoutEffect是render和繪制到屏幕之間。
4、如何自定義hook?
hook只能定義在函數(shù)式組件中,不能在普通函數(shù)中使用,如果我們想要使用到上面的hook來(lái)封裝一些方法供很多個(gè)組件調(diào)用,這時(shí)候就需要自定義hook,自定義hook的命名就是在函數(shù)名前加 use,函數(shù)名由 saveInfo 改為 useSaveInfo 即可。
以上就是react中常見(jiàn)hook的使用方式的詳細(xì)內(nèi)容,更多關(guān)于react hook的使用的資料請(qǐng)關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://segmentfault.com/a/1190000039788572