React Hooks 學(xué)習(xí)總結(jié)

前言

最近的換寫React項(xiàng)目了,好久沒有寫React,還動(dòng)不動(dòng)就想class??,現(xiàn)在跟上時(shí)代,重新學(xué)習(xí)React Hook.Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。

為什么有Hook?

1、在組件之間復(fù)用狀態(tài)邏輯比較難,需要用到高階組件或者render props,Hook可以無需修改組件結(jié)構(gòu)的情況下,復(fù)用狀態(tài)邏輯。
2、Hook可以將組件中相互關(guān)聯(lián)的部分拆分成更小的函數(shù),方便理解;
3、不再使用class和this,更方便上手和理解;

使用Hook的注意事項(xiàng)

1、只能在最外層函數(shù)中調(diào)用Hook。(不要在循環(huán)、條件判斷、子函數(shù)中調(diào)用)
React在創(chuàng)建hook時(shí),以鏈表的解構(gòu)存儲(chǔ),前一個(gè)hook中next指針指向下一個(gè)hook。在組件更新時(shí)會(huì)進(jìn)行hook的一一對(duì)應(yīng)的比對(duì),所以要保證每次hook數(shù)量一致,位置相同。循環(huán)、條件判斷中使用hook,會(huì)導(dǎo)致數(shù)量和位置的變化,產(chǎn)生錯(cuò)誤。
2、只在react的函數(shù)組件中調(diào)用。

常用的HooK

1.useState

  • 作用:在組件內(nèi)部添加一個(gè)state,React會(huì)在重復(fù)渲染時(shí)保留這個(gè)state。
  • 參數(shù): 1個(gè),state的初始值initialState;
const [state,setState] =useState(initialState)
  • 返回值 :2個(gè)
    1)state 當(dāng)前狀態(tài),在初始渲染時(shí),state就是initialState
    2)setState更新當(dāng)前state的函數(shù),他接受一個(gè)新的state,并且將該組件的一次渲染加入隊(duì)列,他有點(diǎn)類似類組件中this.setState,但并不會(huì)將新舊state合并
//在函數(shù)組件中
const [state,setState] =useState({name:"lily",age:18})
return(
<button onclick={setState({age:20})}>改變年齡<button>
//這里使用useState不會(huì)將新舊state,現(xiàn)在state為{age:20},name屬性就沒有了
//所以需要注意 onclick={setState({...state,age:20})}
)
  • 特點(diǎn)
    1) 每次渲染都是獨(dú)立的閉包,擁有當(dāng)前自己的props\state\函數(shù)
    2)函數(shù)式更新
//如果新的狀態(tài)需要依賴舊的狀態(tài),可以使用函數(shù)式更新
setState(state =>({...state,age:state.age +1}))
//將舊狀態(tài)傳入,返回新的狀態(tài)

3)懶加載初始化state

//如果state中的值需要復(fù)雜計(jì)算
const [state,setState] =useState(()=>  ({name:"lily",age:12}))
  • 性能優(yōu)化(減少渲染次數(shù))
    1)Object.is
    在調(diào)用state的更新函數(shù)時(shí),React會(huì)先進(jìn)行Object.is的比較算法
    如果state不相同,進(jìn)行子組件渲染和effect,否則不進(jìn)行刷新渲染

2)useMemo(緩存值)
某些情況下組件中的值需要依賴state進(jìn)行計(jì)算后得到,由于每次組件更新都會(huì)去重新計(jì)算,為了減少性能消耗,可以使用useMemo緩存值,僅僅當(dāng)依賴改變時(shí),才重新計(jì)算。

  • 參數(shù):計(jì)算函數(shù),依賴數(shù)組
    則函數(shù)僅會(huì)在某個(gè)依賴項(xiàng)改變時(shí)才重新計(jì)算memoized 值。
 const [count, setCount] = useState(0);
//expensive 需要依賴count計(jì)算得到
 const expensive = useMemo(() => {
 let sum = 0;
 for (let i = 0; i < count; i++) {
 sum += i;
 }
 return sum;
 //只有count變化,這?才重新執(zhí)?
 }, [count]);

3)useCallback (緩存回調(diào)函數(shù))
由于每次組件更新都會(huì)內(nèi)部函數(shù)都會(huì)重新創(chuàng)建一次,為了節(jié)省性能,可以使用useCallback,使內(nèi)聯(lián)回調(diào)函數(shù)僅僅在傳入依賴項(xiàng)改變時(shí)進(jìn)行更新。

  • 參數(shù):內(nèi)聯(lián)回調(diào)函數(shù),依賴數(shù)組
    則函數(shù)僅會(huì)在某個(gè)依賴項(xiàng)改變時(shí)才進(jìn)行更新。
 const [count, setCount] = useState(0);
 const addClick = useCallback(() => {
setCount(count+1)
 }, [count]);

4)memo (緩存組件)
父組件更新子組件也會(huì)重新創(chuàng)建,為節(jié)省性能,可用memo將子組件緩存,僅僅當(dāng)props改變時(shí),組件才進(jìn)行更新

const Child = (props)=>{
return <div>{props.age}</div>
}
//這里只會(huì)在props改變的時(shí)候重新渲染
Child =memo(Child)
const Father=()=>{
const [name,setName]=useState("lily")
const [age,setAge]=useState(18)
return(
<>
<button onclick={setAge(age+1)}>改變年齡<button>
<input onChange={e=>(setName(e.target.value))}>改變姓名<button>
<Child age={age}/> 
</>
)
}

5)useRef (緩存ref)
同理,用useRef可對(duì)React.creactRef對(duì)象進(jìn)緩存

2. useReducer

  • 作用:編寫自定義hook或者在某些改變state邏輯復(fù)雜的情況下替代useState,用法類似redux,useState是useReducer的語法糖。
  • 參數(shù): 3個(gè),reducer方法 、initialState初始值,init;
const [state,dispatch] =useReducer(reducer ,initialState)
  • 返回值 :2個(gè)
    1)state當(dāng)前狀態(tài),在初始渲染時(shí),state就是initialState
    2)dispatch派發(fā)方法,接受參數(shù)action,改變當(dāng)前state,并且將該組件的一次渲染加人隊(duì)列。

  • 自定義hook

//實(shí)現(xiàn)一個(gè)useState
const useState=(initalState)=>{
    const reducer = useCallback(_state,action)=>{
        return action.paylaod
    }
    const [state,dispatch] = useReducer(reducer, initalState)
    const setState = (state)=>{
        dispatch({paylaod:state})
    }
    return [state,setState]
}

3.useContext

  • 作用:提供更方便的方法從React上下文中拿到consumer的值
    參數(shù): 1個(gè),context(React.createContext);
const  value=useContext(context)
  • 返回值 :1個(gè) consumer的值

4.useEffect

  • 作用:在React渲染階段,改變DOM、添加訂閱、設(shè)置定時(shí)器等操作會(huì)破壞UI一致性,含有副作用的操作將不被允許,使用useEffect完成副作用的操作,傳入函數(shù)會(huì)在組件渲染后執(zhí)行.
  • 參數(shù): 2個(gè),didUpdate包含副作用的回調(diào)函數(shù),在組件渲染后執(zhí)行,[state]依賴項(xiàng),傳入依賴項(xiàng),僅在依賴項(xiàng)改變后的執(zhí)行
useEffect(didUpdate,[state])
  • 返回值 :1個(gè) 清除副作用函數(shù),在組件銷毀前執(zhí)行,相當(dāng)于componentWillUnmount,可進(jìn)行定時(shí)器,事件監(jiān)聽的移除操作
  • 特點(diǎn)
    由于useEffect的副作用函數(shù)在組件渲染后執(zhí)行,他和class組件中的 生命周期componentDidMount/componentDidUpdate和componentWillUnmount有相同的用途

5.useRef、useImperativeHandle

  • useRef作用:對(duì)ref對(duì)象進(jìn)緩存,ref 對(duì)象在組件的整個(gè)生命周期內(nèi)保持不變。
  • 參數(shù): 1個(gè),initialValue,ref.curren初始化的值
function  FocusButton() {
    const inputEl = useRef(null); 
    const onButtonClick = () => {
        // `current` 指向已掛載到 DOM 上的文本輸入元素
        inputEl.current.focus();
    };
    return (
        <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    );
}

  • useImperativeHandle作用:有些情況下我們需要父組件把ref傳遞給子組件,僅僅使用forwardRef將子組件的ref屬性都暴露給父組件是危險(xiǎn)的,也違反了封裝原則,useImperativeHandle允許在子組件中把自定義實(shí)例附加到父組件傳過來的ref上,減少暴露給父組件的屬性
  • 參數(shù): 3個(gè),ref, createHandle處理函數(shù),其返回值會(huì)替換父組件傳來的ref.current, [state]依賴項(xiàng)
useImperativeHandle(ref, createHandle, [state])
  • 例子
let Child =  (props, ref) =>{
    const inputRef = useRef();
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus()
        }
    }));
    return <input ref={inputRef} />;
}
Child = forwardRef(Child );

const Father=() => {
    const ref = useRef(null);
    return (
        <>
            <Child ref={ref}/>
             <button onClick={()=>{ ref.current.focus()}}>聚焦</button >
        </>
    )
}

6.useLayoutEffect

  • 作用 :與useEffect相同,都是用來執(zhí)行副作用。
  • 特點(diǎn) :useEffect是在渲染之后調(diào)用effect,也就是下一楨執(zhí)行,是異步的;
    而useLayoutEffect在當(dāng)前幀Render tree計(jì)算布局(Layout)信息后,渲染前,同步調(diào)用effect。官方文檔建議使用標(biāo)準(zhǔn)useEffec。

7.自定義Hook

  • 定義:函數(shù)以u(píng)se開頭并且在函數(shù)內(nèi)部調(diào)用其他hooks,并且返回一個(gè)數(shù)組,可以實(shí)現(xiàn)邏輯復(fù)用。
  • 例子 :
//實(shí)現(xiàn)一個(gè)thunk
const useThunk =(reducer,initalState)=>{
const [state,dispatch] =useReducer(reducer,initalState)
const thunkDispatch = (action)=>{
if( action instanceof Function){ //如果action是函數(shù),傳入新的dispatch執(zhí)行action
   return action (thunkDispatch,()=>state)
}else{
dispatch(action )
}
return [state,thunkDispatch]
}


結(jié)尾

現(xiàn)在就只了解到這么多。還有什么沒寫到的日后更新。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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