教程5 min read
React Hooks 完全指南
什么是 React Hooks?
React Hooks 是 React 16.8 引入的新特性,让你在函数组件中使用状态和其他 React 特性。
Hooks 的出现改变了我们编写 React 组件的方式,使得函数组件变得更加强大和灵活。
为什么需要 Hooks?
- 更简洁的代码 - 函数组件比类组件更简洁
- 更好的逻辑复用 - 自定义 Hooks 可以轻松复用状态逻辑
- 更直观的数据流 - 相关的逻辑可以组织在一起
- 更容易测试 - 纯函数更容易测试
核心 Hooks
useState
管理组件的本地状态:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>当前计数: {count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
<button onClick={() => setCount(count - 1)}>
减少
</button>
<button onClick={() => setCount(0)}>
重置
</button>
</div>
);
}
useEffect
处理副作用操作:
import { useEffect, useState } from 'react';
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchUser() {
try {
setLoading(true);
setError(null);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('获取用户信息失败');
}
const data = await response.json();
setUser(data);
} catch (err) {
setError(err instanceof Error ? err.message : '未知错误');
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]); // 仅在 userId 变化时重新执行
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
if (!user) return <div>未找到用户</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
useContext
访问 Context 中的数据:
import { useContext, createContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext<{
theme: string;
toggleTheme: () => void;
}>({
theme: 'light',
toggleTheme: () => {},
});
// Provider 组件
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 使用 Context
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
className={`btn-${theme}`}
>
当前主题: {theme},点击切换
</button>
);
}
useReducer
管理复杂状态逻辑:
import { useReducer } from 'react';
interface State {
count: number;
step: number;
}
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset' }
| { type: 'setStep'; payload: number };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'decrement':
return { ...state, count: state.count - state.step };
case 'reset':
return { ...state, count: 0 };
case 'setStep':
return { ...state, step: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });
return (
<div>
<p>计数: {state.count}</p>
<p>步长: {state.step}</p>
<input
type="number"
value={state.step}
onChange={(e) => dispatch({
type: 'setStep',
payload: Number(e.target.value)
})}
/>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>重置</button>
</div>
);
}
自定义 Hooks
将组件逻辑提取到可复用的函数中:
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error('读取 localStorage 失败:', error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function
? value(storedValue)
: value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error('写入 localStorage 失败:', error);
}
};
return [storedValue, setValue] as const;
}
// 使用示例
function App() {
const [name, setName] = useLocalStorage('name', '');
const [age, setAge] = useLocalStorage('age', 0);
return (
<div>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="输入姓名"
/>
<input
type="number"
value={age}
onChange={e => setAge(Number(e.target.value))}
placeholder="输入年龄"
/>
<p>姓名: {name}, 年龄: {age}</p>
</div>
);
}
Hooks 使用规则
- 只在顶层调用 - 不要在循环、条件或嵌套函数中调用 Hooks
- 只在 React 函数中调用 - 不要在普通 JavaScript 函数中调用
- 以
use开头命名 - 自定义 Hooks 必须以use开头
总结
React Hooks 让函数组件拥有了类组件的所有能力,同时代码更简洁、逻辑更清晰。掌握 Hooks 是现代 React 开发的基础。
通过合理使用 Hooks,我们可以:
- 更好地组织代码逻辑
- 提高代码的可复用性
- 简化组件的状态管理
- 提升开发效率