Hook 是 React 16.8 開(kāi)始支持的新特性,旨在用函數(shù)式組件代替類式組件。本文記錄常用Hook用法及注意點(diǎn)
1、useState
返回一個(gè) state,以及更新 state 的函數(shù)。給函數(shù)式組件增加內(nèi)部狀態(tài),狀態(tài)一旦變化,組件會(huì)重新渲染
const [state, setState] = useState(initialState);
- useState 可以接受單值或函數(shù),若是函數(shù),則以函數(shù)返回值作為初始state,且該函數(shù)僅在初始化時(shí)執(zhí)行一次,后期組件更新不再執(zhí)行
- setState 可以接受單值或函數(shù),若是函數(shù),則函數(shù)第一個(gè)參數(shù)是上一個(gè)state值,返回值若與上一個(gè)state值一樣,則組件不重新渲染
- 與類式組件的setState不同,函數(shù)組件的 setState 不會(huì)合并所有狀態(tài),不過(guò)可以這樣做到:
setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});
- 使用 Object.is方法判斷前后 state 是否相等
2、useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
當(dāng)傳入第三個(gè)函數(shù)參數(shù),則state=init(initialArg);否則 state = initialArg
- useReducer 是 useState 的替代方案
- reducer: (state, action) => newState
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
3、useEffect
接收一個(gè)包含命令式、且可能有副作用代碼的函數(shù)A和一個(gè)依賴數(shù)組B作為參數(shù)。函數(shù)A會(huì)在組件渲染到屏幕之后執(zhí)行。每次重新渲染組件時(shí),B中的值不變化,則函數(shù)A不會(huì)重新執(zhí)行
useEffect(()=>{
// 副作用代碼,如訂閱、dom操作、定時(shí)器、獲取數(shù)據(jù)等
},[]);
- 函數(shù)A可以返回一個(gè)函數(shù),用于清除副作用,如取消訂閱、定時(shí)器等操作
- 如果組件多次渲染(通常如此),則在執(zhí)行下一個(gè) effect 之前,上一個(gè) effect 就已被清除
- 函數(shù)A會(huì)在瀏覽器繪制后延遲執(zhí)行,但會(huì)保證在任何新的渲染前執(zhí)行。因此不應(yīng)在函數(shù)A中執(zhí)行阻塞瀏覽器更新屏幕的操作,不然用戶會(huì)感覺(jué)到視覺(jué)上的不一致
4、useLayoutEffect
函數(shù)簽名與 useEffect 相同,會(huì)在所有的 DOM 變更之后同步調(diào)用 effect??梢允褂盟鼇?lái)讀取 DOM 布局并同步觸發(fā)重渲染。在瀏覽器執(zhí)行繪制之前,useLayoutEffect 內(nèi)部的更新計(jì)劃將被同步刷新。
- useEffect 是異步的,useLayoutEffect 是同步的
5、useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useCallback 返回第一個(gè)函數(shù)A參數(shù)的記憶化版本,該函數(shù)A的引用只有在依賴數(shù)組的元素發(fā)生改變時(shí)才會(huì)改變。useCallback本身會(huì)在每次重新渲染時(shí)執(zhí)行
6、useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
返回一個(gè)記憶化的值V,該值只有在依賴數(shù)組的元素發(fā)生改變時(shí)才會(huì)改變。useCallback本身會(huì)在每次重新渲染時(shí)執(zhí)行。
- useCallBack 用于記憶函數(shù),useMemo 用于記憶需要復(fù)雜計(jì)算而來(lái)的衍生值
- useMemo 的第一個(gè)函數(shù)參數(shù)在初次渲染時(shí)也會(huì)執(zhí)行
7、useRef
useRef 返回一個(gè)可變的 ref 對(duì)象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。
// const refContainer = useRef(initialValue);
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已掛載到 DOM 上的文本輸入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
- ref 對(duì)象的 current 屬性值是可變的,可保存任何值
- ref 對(duì)象內(nèi)容發(fā)生變化時(shí),useRef 并不會(huì)通知你。變更 .current 屬性不會(huì)引發(fā)組件重新渲染。
- 回調(diào) ref
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
8、自定義 Hook
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
- 用 useXXX 命名
- 通常內(nèi)部調(diào)用 useState,useEffect 等內(nèi)置hook
- 返回值隨意