react中常見hook的使用方式
1、什麼是hook?
react hook是react 16.8推出的方法,能夠讓函數式組件像類式組件一樣擁有state、ref、生命周期等屬性。
2、為什麼要出現hook?
函數式組件是全局當中一個普通函數,在非嚴格模式下this指向window,但是react內部開啟瞭嚴格模式,此時this指向undefined,無法像類式組件一樣使用state、ref,函數式組件定義的變量都是局部的,當組件進行更新時會重新定義,也無法存儲,所以在hook出現之前,函數式組件有很大的局限性,通常情況下都會使用類式組件來進行代碼的編寫。
3、有哪些常用的hook?
(1) useState
使函數式組件也能保存狀態的一個hook,這個hook的入參是狀態的初始值,返回值是一個數組,數組裡第一個參數為狀態的值,第二個參數為修改狀態的方法。
// 初始化 const [ count, setCount ] = useState(0) // 更新 setCount(count+1)
(2) useEffect
函數式組件用來模擬生命周期的hook,可以模擬組件掛載完成、更新完成、即將卸載三個階段,即componentDidMount、componentDidUpdate、componentWillUnmount。
useEffect的一個參數為函數,表示組件掛載、更新時執行的內容,在函數裡再返回一個函數,表示組件即將卸載時調用的函數。
第二個參數為可選項,可傳入數組,數組裡可以為空,表示不依賴任何狀態的變化,即隻在組件即將掛載時執行,後續任何狀態發生瞭變化,都不調用此hook。數組裡也可以定義一或多個狀態,表示每次該狀態變化時,都會執行此hook。
useEffect(()=>{ // 這樣模擬的是 componentDidMount }, []) useEffect(()=>{ // 這樣模擬的是componentDidMount 以及當count發生變化時執行componentDidUpdate }, [count]) useEffect(()=>{ return ()=>{ // 這樣模擬的是 componentWillUnmount } }, [])
(3) useContext
在沒有hook之前,我們通常都會通過 xxxContext.Provider 和 xxxContext.Consumer 的方式來傳遞和獲取context的值,使用hook之後,傳遞context的方式不變,但子元素獲取context的方式變得更加的簡潔。
// 以前的定義方式 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用法比較類似,返回一個ref對象,這個對象在函數的整個生命周期都不變,根據這個特性,有兩種比較常見的用法。
① 用於dom元素或者組件上,通過current屬性可以獲取到dom元素或者類式組件的實例對象。需要註意的是,無論是useRef還是createRef或者是回調形式、字符串形式的ref,都是不能直接給函數式組件定義的,因為函數式組件的this指向undefined,沒有實例對象,隻能通過forwardRef定義到函數式組件中的某個dom元素。
// 這樣就將傳遞給函數式組件的ref綁定在瞭函數式組件內部的input標簽上 import React, { useRef, forwardRef } from 'react' // 使用函數表達式的方式定義瞭一個函數式組件 const InputCom = forwardRef((props, ref) => { return <input ref={ref}/> }) export default function refDemo(){ const comRef = useRef() return(<div> <InputCom ref={comRef}/> </div>) }
② 保存一個數據,該數據如果不手動修改,它在整個生命周期中都不變
const [ count, setCount ] = useState(0) const prevCount = useState(count) // 當count發生變化時,組件更新,對count的前一次數據進行保存 useEffect(()=>{ prevCount.current = count }, [count])
(5) useReducer
useReducer相當於是useState的升級版,作用與useState類似,都是用來保存狀態,但它的不同點在於可以定義一個reducer的純函數,來處理復雜數據。
// 定義一個處理數據的reducer純函數 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 } } // 初始化狀態 const [ count, dispatch ] = useReducer(reducer, { count: 0 }) // 修改狀態,此時的修改需要派發一個action,讓傳入的reducer函數進行處理 dispatch({ type: 'increment' })
(6) useCallback
函數式組件中,每一次更新狀態,自定義的函數都要進行重新的聲明和定義,如果函數作為props傳遞給子組件,會造成子組件不必要的重新渲染,有時候子組件並沒有使用到父組件發生變化的狀態,此時可以使用useCallback來進行性能優化,它會為函數返回一個記憶的值,如果依賴的狀態沒有發生變化,那麼則不會重新創建該函數,也就不會造成子組件不必要的重新渲染。
import React, { useState, useCallback, memo } from 'react' const AddBtn = memo((props)=>{ // 使用函數表達式的方式定義瞭一個函數式組件 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被調用瞭') setCount(count+1) } const increment2 = useCallback(()=>{ // 使用瞭useCallback來優化的函數 console.log('increment2被調用瞭') setCount(count+1) },[count]) return(<div> <div>當前計數:{count}</div> <AddBtn increment={increment1} name="1"/> <AddBtn increment={increment2} name="2"/> <button onClick={e => setShow(!show)}>切換show</button> </div>) } // 當show這個狀態發生變化時,子組件increment1會重新渲染,increment2不會重新渲染
(7) useMemo
useMemo也是返回一個記憶的值,如果依賴的內容沒有發生改變的話,這個值也不會發生變化,useMemo與useCallback的不同點在於useMemo需要在傳入的函數裡需要return 一個值,這個值可以是對象、函數,格式如下。
useMemo(()=>{ return { count } }, [count]) // 使用useCallback時 const increment2 = useCallback(()=>{ setCount(count+1) },[count]) // 使用useMemo模擬useCallback const increment2 = useCallback(()=>{ return ()=>{ setCount(count+1) } },[count]) // useMemo的應用場景,當要進行一些復雜的計算時, //計算的值沒有發生變化,並不需要每一次更新都重新計算 import React, { useState, useMemo } from 'react' const calculateNum = (count) => { console.log('total重新計算瞭') 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
這個是與forwardRef配合來使用的,當我們對函數式組件使用forwardRef將ref指定瞭dom元素之後,那就父組件就可以任意的操作指定的dom元素,使用useImperativeHandle就是為瞭控制這樣的一種行為,指定父元素可操作的子元素的方法。
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
這個方法與useEffect類似,隻是執行的順序稍有不同,useEffect是在組件渲染繪制到屏幕上之後,useLayoutEffect是render和繪制到屏幕之間。
4、如何自定義hook?
hook隻能定義在函數式組件中,不能在普通函數中使用,如果我們想要使用到上面的hook來封裝一些方法供很多個組件調用,這時候就需要自定義hook,自定義hook的命名就是在函數名前加 use,函數名由 saveInfo 改為 useSaveInfo 即可。
以上就是react中常見hook的使用方式的詳細內容,更多關於react hook的使用的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- None Found