React Hooks 常考內容
React Hooks 是 React 16.8 引入的重要特性,用于在函數組件中使用狀態和其他 React 特性。以下是面試中常考的核心內容:
基礎 Hook
useState
: 用于管理組件內部狀態,返回狀態變量和更新狀態的函數。useEffect
: 處理副作用(如數據請求、DOM 操作、訂閱等),可以模擬生命周期方法。useContext
: 允許組件訂閱 React 上下文,避免多層 props 傳遞。
進階 Hook
useReducer
: 復雜狀態邏輯管理,類似于 Redux 的 reducer 模式。useCallback
: 緩存函數,避免不必要的重新渲染。useMemo
: 緩存計算結果,優化性能。useRef
: 創建可變的引用對象(如訪問 DOM 或保存變量)。useLayoutEffect
: 類似useEffect
,但同步執行,適用于 DOM 布局相關操作。
自定義 Hook
- 封裝可復用的邏輯,遵循命名規則
useXxx
。
React Hooks 知識框架詳解
核心機制與原理
- Hook 調用順序:Hooks 必須在函數組件的頂層調用,不可嵌套在條件或循環中。React 依賴調用順序來跟蹤狀態。
- 閉包與依賴數組:Hooks 依賴 JavaScript 閉包機制,依賴數組(如
useEffect
的第二個參數)控制副作用觸發時機。
性能優化
- 依賴數組優化:合理設置依賴數組,避免不必要的副作用執行。
useCallback
與useMemo
:緩存函數或計算結果,避免子組件因引用變化重新渲染。React.memo
配合 Hooks:減少組件重復渲染。
常見問題與解決方案
- 無限循環:因依賴數組設置不當導致
useEffect
反復觸發。 - 狀態延遲更新:批量更新機制下,連續調用
setState
可能合并為一次更新。 useRef
保存變量:解決閉包陷阱(如定時器中訪問最新狀態)。
設計模式
- 狀態邏輯復用:通過自定義 Hook 封裝邏輯(如
useFetch
數據請求)。 - 復雜狀態管理:
useReducer
適合多狀態關聯的場景,可替代部分 Redux 需求。 - 上下文共享:
useContext
+useReducer
實現輕量級狀態管理。
代碼示例
useState
基礎用法
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>;
useEffect
清理副作用
useEffect(() => {const timer = setInterval(() => console.log('Tick'), 1000);return () => clearInterval(timer); // 清理
}, []);
自定義 Hook 示例
function useToggle(initialValue = false) {const [value, setValue] = useState(initialValue);const toggle = () => setValue(!value);return [value, toggle];
}
面試高頻問題
- 為什么 Hooks 不能放在條件或循環中調用?
useEffect
和useLayoutEffect
的區別?- 如何用 Hooks 模擬
componentDidMount
? - 如何解決閉包導致的 stale state 問題?
- 自定義 Hook 的設計原則是什么?
Hooks 不能放在條件或循環中調用的原因
React Hooks 的調用順序必須保持一致,這是 React 內部依賴調用順序管理狀態的核心機制。條件或循環會導致 Hooks 的調用順序在不同渲染中發生變化,從而破壞狀態的一致性,引發難以追蹤的 bug。
Hooks 的規則通過 ESLint 插件 eslint-plugin-react-hooks
強制執行,確保開發者遵循這一原則。違反規則會導致 React 拋出錯誤,提示 Hooks 的調用數量不一致。
useEffect 和 useLayoutEffect 的區別
useEffect
是異步執行的,在瀏覽器完成渲染后才觸發副作用,適合處理數據訂閱、手動 DOM 操作等非緊急任務。
useLayoutEffect
是同步執行的,在 DOM 更新后但瀏覽器繪制前觸發,適合需要同步讀取布局或避免視覺閃爍的場景。濫用可能導致性能問題。
兩者的函數簽名相同,區別僅在于執行時機。通常優先使用 useEffect
,僅在布局相關需求時選擇 useLayoutEffect
。
用 Hooks 模擬 componentDidMount
將 useEffect
的依賴數組設為空,確保副作用僅在組件掛載時執行一次:
useEffect(() => {// 此處代碼僅在組件掛載時運行
}, []); // 空依賴數組
注意:這與 componentDidMount
的語義并不完全相同,例如在 SSR 場景下仍需額外處理。
解決閉包導致的 stale state 問題
閉包問題通常出現在異步操作中訪問舊狀態值。解決方法包括:
- 使用函數式更新:確保獲取最新狀態
setCount(prevCount => prevCount + 1);
- 通過 ref 保存最新值:配合
useEffect
同步狀態
const countRef = useRef(count);
useEffect(() => {countRef.current = count;
}, [count]);
- 合理設置依賴數組:確保回調重新創建時捕獲最新值
自定義 Hook 的設計原則
- 單一職責:每個 Hook 應解決一個特定問題,避免功能混雜
- 命名清晰:使用
use
前綴明確標識為 Hook - 狀態隔離:不同組件使用同一 Hook 時狀態獨立
- 組合優先:通過組合基礎 Hooks 實現復雜邏輯
- 文檔完備:明確輸入輸出和使用約束
- 性能優化:合理使用
useMemo
/useCallback
避免無效計算
自定義 Hook 本質上是對狀態邏輯的復用,設計時應遵循 React Hooks 的既有規則。