文章目錄
- 一文講清楚React Hooks
- 一、什么是 React Hooks?
- 二、常用基礎 Hooks
- 2.1 useState:狀態管理
- 基本用法
- 特點
- 2.2 useEffect:副作用處理
- 基本用法
- 依賴數組說明
- 2.3 useContext:上下文共享
- 基本用法
- 特點
- 三、其他常用 Hooks
- 3.1 useReducer:復雜狀態管理
- 3.2 useCallback:記憶化回調函數
- 3.3 useMemo:記憶化計算結果
- 3.4 useRef:獲取DOM元素與存儲可變值
- 四、其他實用 Hooks
- 五、Hooks 使用規則
一文講清楚React Hooks
一、什么是 React Hooks?
React Hooks 是 React 16.8 版本引入的新特性,它允許開發者在不編寫類組件的情況下使用狀態(state)和其他 React 特性(如生命周期、上下文等)。Hooks 的出現解決了類組件中存在的代碼復用困難、邏輯分散、生命周期函數臃腫等問題,讓函數組件擁有了與類組件同等的能力。
二、常用基礎 Hooks
2.1 useState:狀態管理
`useState` 是最基礎的 Hook,用于在函數組件中添加狀態管理。
基本用法
import { useState } from 'react';function Counter() {// 聲明一個名為count的狀態變量,初始值為0// setCount是更新count的函數const [count, setCount] = useState(0);return (<div><p>你點擊了 {count} 次</p><button onClick={() => setCount(count + 1)}>點擊我</button></div>);
}
特點
- 返回值:返回一個數組,第一個元素是當前狀態值,第二個元素是更新狀態的函數
- 狀態更新:
- 調用更新函數會重新渲染組件
- 狀態更新是異步的,多次連續更新會被合并
- 如果新狀態依賴于舊狀態,應使用函數形式:`setCount(prevCount => prevCount + 1)`
- 初始化:初始值只在組件首次渲染時生效
2.2 useEffect:副作用處理
`useEffect` 用于處理組件中的副作用操作,如數據獲取、訂閱、DOM 操作等,相當于類組件中的 `componentDidMount`、`componentDidUpdate` 和 `componentWillUnmount` 的組合。
基本用法
import { useState, useEffect } from 'react';function UserProfile({ userId }) {const [user, setUser] = useState(null);// 副作用函數useEffect(() => {// 獲取用戶數據(副作用操作)const fetchUser = async () => {const response = await fetch(\`/api/users/\${userId}\`);const data = await response.json();setUser(data);};fetchUser();// 清理函數(可選)return () => {// 組件卸載或userId變化時執行,用于取消訂閱、清除定時器等console.log('清理操作');};}, [userId]); // 依賴數組:只有userId變化時,才會重新執行副作用if (!user) return <div>Loading...</div>;return <div>{user.name}</div>;
}
依賴數組說明
- 空數組 `[]`:副作用只在組件掛載時執行一次,清理函數在組件卸載時執行(類似 `componentDidMount` 和 `componentWillUnmount`)
- 包含特定值 `[value1, value2]`:組件掛載時執行,且當數組中的值發生變化時重新執行(類似 `componentDidUpdate`)
- 不提供依賴數組:每次組件渲染都會執行副作用
2.3 useContext:上下文共享
`useContext` 用于在函數組件中獲取上下文(Context)的值,避免了通過 props 層層傳遞數據的麻煩。
基本用法
import { createContext, useContext } from 'react';// 1. 創建上下文
const ThemeContext = createContext('light');// 2. 提供上下文值
function App() {return (<ThemeContext.Provider value="dark"><ThemedComponent /></ThemeContext.Provider>);
}// 3. 消費上下文值
function ThemedComponent() {// 使用useContext獲取上下文值const theme = useContext(ThemeContext);return <div>當前主題:{theme}</div>;
}
特點
- 當 `ThemeContext.Provider` 的 `value` 發生變化時,所有使用 `useContext(ThemeContext)` 的組件都會重新渲染
- `useContext` 接收一個上下文對象(由 `createContext` 創建),并返回該上下文的當前值
三、其他常用 Hooks
3.1 useReducer:復雜狀態管理
`useReducer` 是 `useState` 的替代方案,適用于處理復雜狀態邏輯或多個子值組成的狀態對象。
import { useReducer } from 'react';// 1. 定義reducer函數:接收當前狀態和動作,返回新狀態
function todoReducer(state, action) {switch (action.type) {case 'ADD_TODO':return [...state, { id: Date.now(), text: action.text, done: false }];case 'TOGGLE_TODO':return state.map(todo =>todo.id === action.id ? { ...todo, done: !todo.done } : todo);default:return state;}
}function TodoList() {// 2. 使用useReducer:接收reducer和初始狀態,返回當前狀態和dispatch函數const [todos, dispatch] = useReducer(todoReducer, []);return (<div><button onClick={() => dispatch({ type: 'ADD_TODO', text: '新任務' })}>添加任務</button><ul>{todos.map(todo => (<likey={todo.id}style={{ textDecoration: todo.done ? 'line-through' : 'none' }}onClick={() => dispatch({ type: 'TOGGLE_TODO', id: todo.id })}>{todo.text}</li>))}</ul></div>);
}
3.2 useCallback:記憶化回調函數
`useCallback` 用于記憶化回調函數,避免在每次渲染時創建新的函數實例,從而優化子組件的性能(配合 `React.memo` 使用)。
import { useCallback, useState } from 'react';function Parent() {const [count, setCount] = useState(0);// 記憶化回調函數:只有當依賴項變化時,才會創建新的函數const handleClick = useCallback(() => {console.log('點擊事件');}, []); // 空依賴數組:函數只會被創建一次return (<div><Child onClick={handleClick} /><button onClick={() => setCount(count + 1)}>計數:{count}</button></div>);
}// 使用React.memo優化子組件,避免不必要的重渲染
const Child = React.memo(({ onClick }) => {console.log('Child 渲染');return <button onClick={onClick}>點擊我</button>;
});
3.3 useMemo:記憶化計算結果
`useMemo` 用于記憶化計算結果,避免在每次渲染時重復執行昂貴的計算。
import { useMemo, useState } from 'react';function ExpensiveCalculation({ numbers }) {// 記憶化計算結果:只有當numbers變化時,才會重新計算const sum = useMemo(() => {console.log('執行計算');return numbers.reduce((acc, num) => acc + num, 0);}, [numbers]); // 依賴數組return <div>總和:{sum}</div>;
}function App() {const [count, setCount] = useState(0);const [numbers] = useState([1, 2, 3, 4, 5]);return (<div><ExpensiveCalculation numbers={numbers} /><button onClick={() => setCount(count + 1)}>計數:{count}</button></div>);
}
3.4 useRef:獲取DOM元素與存儲可變值
`useRef` 主要有兩個用途:獲取DOM元素引用和存儲不需要引起重渲染的可變值。
import { useRef, useState, useEffect } from 'react';function TextInputWithFocusButton() {// 1. 獲取DOM元素引用const inputEl = useRef(null);// 2. 存儲可變值(不會引起重渲染)const renderCount = useRef(0);const [text, setText] = useState('');useEffect(() => {renderCount.current += 1; // 修改ref的值不會觸發重渲染});const focusInput = () => {// 通過current屬性訪問DOM元素inputEl.current.focus();};return (<div><inputref={inputEl}value={text}onChange={(e) => setText(e.target.value)}/><button onClick={focusInput}>聚焦輸入框</button><p>渲染次數:{renderCount.current}</p></div>);
}
四、其他實用 Hooks
Hook | 用途說明 |
---|---|
`useImperativeHandle` | 自定義暴露給父組件的實例值,配合 `forwardRef` 使用 |
`useLayoutEffect` | 與 `useEffect` 類似,但會在DOM更新后同步執行,可能阻塞瀏覽器繪制 |
`useDebugValue` | 在React DevTools中為自定義Hook添加標簽,便于調試 |
五、Hooks 使用規則
為了確保 React 能夠正確識別和管理 Hooks,必須遵守以下規則:
- 只能在函數組件或自定義 Hook 的頂層調用 Hooks
- 不能在條件語句、循環、嵌套函數中調用 Hooks
- 錯誤示例:
function MyComponent() {if (condition) {const [count, setCount] = useState(0); // ? 不能在條件中調用}// ...}```