Hooks的作用
在沒有hooks之前吓笙,函數(shù)組件只能夠接收props 渲染UI舵变,以及觸發(fā)父組件傳過來的事件客峭。所有的邏輯都要在類組件中實(shí)現(xiàn)。這樣會使類組件內(nèi)部錯綜復(fù)雜矩屁,每一個類組件都有一套獨(dú)特的狀態(tài)辟宗,相互之間不能復(fù)用。即便React之前出現(xiàn)過mixins等方案吝秕,但是minxins會伴隨著隱式依賴和代碼沖突等問題慢蜓。
另外函數(shù)組件要比類組件輕巧很多,同時更加靈活
hooks的目的:
- 讓函數(shù)組件也能做類組件的事情郭膛,有自己的狀態(tài),可以處理一些副作用氛悬,能獲取ref则剃,也能做數(shù)據(jù)緩存
- 解決邏輯復(fù)用的問題
- 放棄面向?qū)ο缶幊?擁抱函數(shù)式編程
常見的hooks
數(shù)據(jù)驅(qū)動型:
- useState
管理組件的狀態(tài),適用于簡單狀態(tài)的管理如捅。
const [state, setState] = useState(initialState);
- useReducer
用于管理復(fù)雜的狀態(tài)邏輯或涉及多個子狀態(tài)的情況棍现,替代 useState 的一種方式。
const [state, dispatch] = useReducer(reducer, initialState);
狀態(tài)獲取/傳遞型:
- useContext
用于獲取上層組件通過 Context 提供的全局狀態(tài)镜遣,避免層層傳遞 props己肮。
const value = useContext(MyContext);
- useRef
用于獲取和存儲 DOM 節(jié)點(diǎn)或其他持久值,組件重新渲染時不會改變悲关。
const ref = useRef(initialValue);
- useImperativeHandle
與 forwardRef 配合使用谎僻,允許自定義暴露給父組件的實(shí)例值。
import React, { useImperativeHandle, forwardRef, useRef } from 'react';
// Step 1: 使用 forwardRef 包裹子組件
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();
// Step 2: 使用 useImperativeHandle 暴露自定義方法
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
}
}));
return <input ref={inputRef} {...props} />;
});
// 父組件
export default function ParentComponent() {
const inputRef = useRef();
return (
<div>
<CustomInput ref={inputRef} placeholder="Type here..." />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
<button onClick={() => inputRef.current.clear()}>Clear Input</button>
</div>
);
}
副作用型:
- useEffect
用于處理副作用(例如數(shù)據(jù)獲取寓辱、訂閱艘绍、手動修改 DOM 等)。它在組件渲染后執(zhí)行秫筏,依賴數(shù)組變化時觸發(fā)诱鞠。
useEffect(() => {
// Effect
return () => {
// Cleanup
};
}, [dependencies]);
- useLayoutEffect
與 useEffect 類似挎挖,但在所有 DOM 變更后同步執(zhí)行,適用于需要在繪制前進(jìn)行 DOM 操作的情況航夺。
useLayoutEffect(() => {
// Effect
return () => {
// Cleanup
};
}, [dependencies]);
- useInsertionEffect
用于在 CSS-in-JS 庫中注入樣式蕉朵。它在 DOM 元素渲染之前同步運(yùn)行,通常比 useLayoutEffect 更早阳掐,可以避免布局抖動
useInsertionEffect(() => {
// 樣式注入邏輯
return () => {
// 清除邏輯
};
}, [dependencies]);
狀態(tài)派生/狀態(tài)保存
- useMemo
用于緩存計算結(jié)果始衅,僅在依賴變化時重新計算,優(yōu)化性能锚烦。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
- useCallback
用于緩存函數(shù)觅闽,使其在依賴未變更的情況下不會重新創(chuàng)建,適用于傳遞給子組件的回調(diào)函數(shù)涮俄。
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
React 18新增的hooks
useSyncExternalStore
useTranstion
useDeferredValue
useInsertionEffect
useId
除此之外React 18還新增了 concurrent模式
concurrent模式指的是在 React 18 中蛉拙,組件的渲染過程可以同時進(jìn)行多個任務(wù),而不僅僅是一個任務(wù)彻亲。
提高應(yīng)用的性能和用戶體驗(yàn)孕锄。它使 React 能夠在更新 UI 時更靈活地管理渲染資源,從而有效應(yīng)對復(fù)雜界面的卡頓問題苞尝。Concurrent 模式引入了一種“打斷式渲染”的機(jī)制畸肆,使 React 可以根據(jù)任務(wù)的重要性和優(yōu)先級分配渲染時間
可中斷渲染:在傳統(tǒng)的同步渲染中,一次渲染過程是不可中斷的宙址,一旦開始轴脐,整個更新必須完成。在 Concurrent 模式中抡砂,渲染過程是可中斷的大咱。React 可以在合適的時候暫停或放棄某些任務(wù)注益,讓高優(yōu)先級的任務(wù)(如用戶輸入碴巾、動畫)優(yōu)先渲染。
時間分片(Time Slicing):Concurrent 模式將渲染任務(wù)分割成小塊丑搔,并在空閑時逐步執(zhí)行這些小塊厦瓢。通過這種方式,React 可以在處理復(fù)雜渲染的同時保持頁面的響應(yīng)性啤月,不會因?yàn)殇秩竞臅r任務(wù)而出現(xiàn)卡頓煮仇。
自動調(diào)節(jié)優(yōu)先級:React 能夠根據(jù)任務(wù)的類型動態(tài)調(diào)整優(yōu)先級。例如谎仲,當(dāng)用戶與應(yīng)用交互時欺抗,React 會自動將用戶輸入的處理設(shè)為高優(yōu)先級,并暫緩其他不那么緊急的渲染任務(wù)强重。這種動態(tài)調(diào)整確保了應(yīng)用能夠在不同情況下保持流暢绞呈。
優(yōu)點(diǎn):
更流暢的用戶體驗(yàn):減少大規(guī)模渲染任務(wù)帶來的卡頓贸人,使動畫和交互更加平滑。
優(yōu)先級管理:高優(yōu)先級任務(wù)(如用戶輸入)能夠迅速響應(yīng)佃声,而非阻塞在渲染隊(duì)列中艺智。
更智能的資源分配:復(fù)雜組件在渲染時會自動優(yōu)化渲染順序,提高頁面性能
React 的 Concurrent 模式在底層通過 ReactDOM.createRoot 實(shí)現(xiàn)圾亏,并且是向后兼容的十拣。只需使用 ReactDOM.createRoot 代替?zhèn)鹘y(tǒng)的 ReactDOM.render
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
useEffect 與 useLayoutEffect 區(qū)別
執(zhí)行時機(jī):
useEffect:在瀏覽器完成繪制之后執(zhí)行。這意味著 React 會先更新 DOM 和頁面布局志鹃,然后執(zhí)行 useEffect 內(nèi)的副作用夭问。這是異步執(zhí)行的,通常不會阻塞頁面的繪制曹铃。
useLayoutEffect:在 DOM 更新后缰趋、瀏覽器繪制之前執(zhí)行。它會阻塞瀏覽器的繪制陕见,確保在布局和繪制之前完成所有同步的 DOM 操作秘血。這是同步執(zhí)行的,適合需要在渲染前計算布局或進(jìn)行 DOM 操作的情況评甜。
用途:
useEffect:適用于不影響布局的副作用灰粮,例如數(shù)據(jù)獲取、事件監(jiān)聽忍坷、日志記錄等粘舟。這些操作無需在瀏覽器渲染之前執(zhí)行,因此可以在 DOM 更新后異步執(zhí)行佩研。
useLayoutEffect:適合在瀏覽器繪制前需要直接訪問和操作 DOM 的場景柑肴。例如,讀取或調(diào)整 DOM 元素的位置韧骗、尺寸等,以避免頁面的閃爍或布局跳動零聚。這些操作需要在渲染前完成袍暴。
useMemo 與 useCallback memo 區(qū)別
useMemo:
- 緩存計算結(jié)果
- 用于緩存計算結(jié)果,以避免不必要的重復(fù)計算隶症。當(dāng)依賴項(xiàng)沒有變化時政模,useMemo 會返回緩存的值,而不是重新計算蚂会。主要適用于需要進(jìn)行復(fù)雜或昂貴計算的場景淋样。
- 使用場景:適合計算屬性、生成列表胁住、數(shù)據(jù)過濾等需要緩存結(jié)果的場景趁猴。
useCallback:
- 緩存函數(shù)引用
- 用于緩存函數(shù)的引用刊咳,以避免在子組件的 props 變化時觸發(fā)不必要的重渲染。當(dāng)依賴項(xiàng)沒有變化時儡司,useCallback 返回相同的函數(shù)引用娱挨。
- 使用場景:適合傳遞給子組件的回調(diào)函數(shù),特別是在依賴項(xiàng)不變時捕犬,避免重新創(chuàng)建函數(shù)導(dǎo)致子組件重復(fù)渲染跷坝。
React.memo:緩存組件
- React.memo 是一個高階組件,用于緩存組件的渲染結(jié)果碉碉。當(dāng)傳遞給組件的 props 沒有變化時柴钻,React.memo 會跳過重新渲染。
- 適合純展示組件或在 props 不變時無需重新渲染的組件垢粮,以提升性能
使用原則:
- useMemo:用于避免昂貴的計算操作贴届。
- useCallback:用于避免回調(diào)函數(shù)引用的變化引起子組件重渲染。
- React.memo:用于避免在 props 相同的情況下重新渲染組件足丢。
總結(jié):
useMemo 緩存計算結(jié)果 計算后的值 復(fù)雜計算粱腻,避免重復(fù)計算
useCallback 緩存函數(shù)引用 緩存的函數(shù) 避免傳遞給子組件的回調(diào)重復(fù)渲染
React.memo 緩存整個組件 緩存的組件 純展示組件,props 不變時跳過渲染