React Hooks 是 React 16.8 引入的一項革命性特性,它允許你在函數組件中使用狀態(state)和其他 React 特性,而無需編寫 class 組件。下面將詳細解讀 React Hooks 的核心概念、常用 Hooks 及其工作原理。
一、Hooks 的核心概念
1. 什么是 Hooks
Hooks 是特殊的函數,以"use"開頭(如?useState
,?useEffect
),讓你能夠"鉤入" React 的狀態和生命周期特性。
2. Hooks 的基本規則
-
只在最頂層調用 Hooks:不要在循環、條件或嵌套函數中調用 Hook
-
只在 React 函數組件或自定義 Hook 中調用 Hooks
二、常用內置 Hooks 詳解
1. useState
const [state, setState] = useState(initialState);
-
用于在函數組件中添加局部狀態
-
返回一個狀態值和一個更新該狀態的函數
-
參數可以是初始值或返回初始值的函數(惰性初始化)
示例:
function Counter() {const [count, setCount] = useState(0);return (<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>);
}
2. useEffect
useEffect(() => {// 副作用邏輯return () => {// 清理函數(可選)};
}, [dependencies]);
-
用于處理副作用(數據獲取、訂閱、手動修改 DOM 等)
-
相當于 class 組件中的?
componentDidMount
,?componentDidUpdate
?和?componentWillUnmount
?的組合 -
第二個參數是依賴數組,控制 effect 的執行時機
示例:
function Example() {const [count, setCount] = useState(0);useEffect(() => {document.title = `You clicked ${count} times`;return () => {// 清理工作};}, [count]); // 僅在 count 更改時更新
}
3. useContext
const value = useContext(MyContext);
-
用于訂閱 React 的 Context 對象
-
接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當前值
示例:
const ThemeContext = React.createContext('light');function ThemedButton() {const theme = useContext(ThemeContext);return <button className={theme}>I am styled by theme context!</button>;
}
4. useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
-
useState 的替代方案,適合復雜 state 邏輯
-
接收一個形如?
(state, action) => newState
?的 reducer 函數 -
返回當前 state 和配套的 dispatch 方法
示例:
function counterReducer(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(counterReducer, {count: 0});return (<>Count: {state.count}<button onClick={() => dispatch({type: 'increment'})}>+</button><button onClick={() => dispatch({type: 'decrement'})}>-</button></>);
}
5. useCallback
const memoizedCallback = useCallback(() => {doSomething(a, b);
}, [a, b]);
-
返回一個 memoized 回調函數
-
僅在依賴項改變時才會更新
-
用于優化子組件渲染,避免不必要的重新渲染
6. useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
-
返回一個 memoized 值
-
僅在依賴項改變時才會重新計算
-
用于性能優化,避免每次渲染都進行高開銷計算
7. useRef
const refContainer = useRef(initialValue);
-
返回一個可變的 ref 對象,其?
.current
?屬性被初始化為傳入的參數 -
常用于訪問 DOM 節點或存儲可變值而不引起重新渲染
示例:
function TextInputWithFocusButton() {const inputEl = useRef(null);const onButtonClick = () => {inputEl.current.focus();};return (<><input ref={inputEl} type="text" /><button onClick={onButtonClick}>Focus the input</button></>);
}
三、Hooks 的工作原理
1. Hooks 的調用順序
React 依賴于 Hooks 的調用順序來正確關聯狀態和對應的 Hook。這就是為什么不能在條件或循環中調用 Hook。
2. Hooks 的實現機制
-
每個組件有一個"記憶單元格"列表(可以看作是一個數組)
-
每次調用 Hook 時,它都會讀取當前的單元格(或初始化它),然后將指針移動到下一個
-
這就是為什么 Hook 的調用順序必須一致
3. 自定義 Hook
可以創建自己的 Hook 來復用狀態邏輯。自定義 Hook 是一個名稱以"use"開頭的 JavaScript 函數,它可以調用其他 Hook。
示例:
function useFriendStatus(friendID) {const [isOnline, setIsOnline] = useState(null);useEffect(() => {function handleStatusChange(status) {setIsOnline(status.isOnline);}ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);return () => {ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);};}, [friendID]);return isOnline;
}
四、Hooks 的優勢
-
簡化組件邏輯:解決了 class 組件中生命周期函數經常包含不相關的邏輯的問題
-
復用狀態邏輯:通過自定義 Hook 可以輕松復用狀態邏輯,無需高階組件或 render props
-
更直觀的代碼:Hooks 讓你根據代碼的用途而非生命周期方法來組織代碼
-
更小的打包體積:函數組件通常比 class 組件更輕量
五、Hooks 的最佳實踐
-
按功能而非生命周期組織代碼:將相關的邏輯放在同一個 useEffect 中
-
合理使用依賴數組:確保 useEffect 和 useCallback/useMemo 的依賴項完整且準確
-
避免過度優化:不要過早使用 useMemo 和 useCallback,先測量再優化
-
自定義 Hook 命名以"use"開頭:這是 React 識別 Hook 的方式
-
考慮使用 eslint-plugin-react-hooks:幫助檢測 Hook 規則的違反