React Hooks 完全指南:从基础到高级应用引言React Hooks 是 React 16.8 引入的革命性特性,它彻底改变了我们编写 React 组件的方式。本文将全面深入地介绍 React Hooks 的核心概念、使用规则和最佳实践。什么是 React HooksHooks 是让你在不编写 class 的情况下使用 state 以及其他的 React 特性的函数。它们让你能够将组件逻辑提取到可重用的函数中。核心优势简化组件逻辑:告别复杂的 class 组件生命周期代码复用:轻松共享状态逻辑更清晰的代码结构:按功能组织代码,而非生命周期方法更好的 TypeScript 支持:更简单的类型定义基础 Hooks 详解useState:状态管理的基础import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: '', age: 0 });
return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>减少</button>
</div>
);
}
最佳实践:使用函数式更新避免闭包问题将相关状态分组到对象中避免过度使用 useState,考虑使用 useReduceruseEffect:副作用处理的艺术import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
if (!cancelled) {
setUser(userData);
setLoading(false);
}
} catch (error) {
if (!cancelled) {
console.error('获取用户数据失败:', error);
setLoading(false);
}
}
}
fetchUser();
// 清理函数
return () => {
cancelled = true;
};
}, [userId]); // 依赖数组
if (loading) return <div>加载中...</div>;
if (!user) return <div>用户不存在</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
useEffect 依赖数组详解:无依赖数组:每次渲染后都执行空数组 []:仅在挂载和卸载时执行有依赖数组:依赖变化时执行useContext:跨组件状态共享// 创建 Context
const ThemeContext = React.createContext();
const UserContext = React.createContext();
// 提供者组件
function AppProviders({ children }) {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
</ThemeContext.Provider>
);
}
// 消费者组件
function Header() {
const { theme } = useContext(ThemeContext);
const { user } = useContext(UserContext);
return (
<header className={`header ${theme}`}>
{user ? `欢迎, ${user.name}` : '请登录'}
</header>
);
}
高级 Hooks 技巧useReducer:复杂状态管理const initialState = { count: 0, history: [] };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1,
history: [...state.history, `增加到 ${state.count + 1}`]
};
case 'decrement':
return {
count: state.count - 1,
history: [...state.history, `减少到 ${state.count - 1}`]
};
case 'reset':
return initialState;
default:
throw new Error();
}
}
function CounterWithHistory() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>当前计数:{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>重置</button>
<div>
<h3>操作历史:</h3>
<ul>
{state.history.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
</div>
);
}
自定义 Hooks:逻辑复用的利器// 自定义 Hook:使用本地存储
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error('Error reading from localStorage', error);
return initialValue;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Error saving to localStorage', error);
}
};
return [storedValue, setValue];
}
// 自定义 Hook:网络请求
function useApi(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchData() {
try {
setLoading(true);
setError(null);
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!cancelled) {
setData(result);
setLoading(false);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
setLoading(false);
}
}
}
fetchData();
return () => {
cancelled = true;
};
}, [url]);
return { data, loading, error, refetch: () => fetchData() };
}
Hooks 规则和最佳实践两条黄金规则只在最顶层调用 Hook:不要在循环、条件或嵌套函数中调用 Hook只在 React 函数中调用 Hook:在 React 函数组件或自定义 Hook 中调用性能优化技巧// 使用 useMemo 缓存计算结果
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
// 使用 useCallback 缓存函数
const handleSubmit = useCallback((values) => {
submitForm(values);
}, [submitForm]);
// 使用 useRef 存储可变值
const inputRef = useRef(null);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
实战案例:Todo 应用function TodoApp() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const inputRef = useRef();
const filteredTodos = useMemo(() => {
return todos.filter(todo => {
if (filter === 'completed') return todo.completed;
if (filter === 'active') return !todo.completed;
return true;
});
}, [todos, filter]);
const addTodo = useCallback((text) => {
if (text.trim()) {
setTodos(prev => [...prev, {
id: Date.now(),
text: text.trim(),
completed: false
}]);
inputRef.current.value = '';
}
}, []);
const toggleTodo = useCallback((id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
}, []);
const removeTodo = useCallback((id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
}, []);
return (
<div className="todo-app">
<h2>待办事项</h2>
<div className="add-todo">
<input ref={inputRef} placeholder="添加新任务..." />
<button onClick={() => addTodo(inputRef.current.value)}>
添加
</button>
</div>
<div className="filters">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => setFilter('all')}
>
全部
</button>
<button
className={filter === 'active' ? 'active' : ''}
onClick={() => setFilter('active')}
>
未完成
</button>
<button
className={filter === 'completed' ? 'active' : ''}
onClick={() => setFilter('completed')}
>
已完成
</button>
</div>
<ul className="todo-list">
{filteredTodos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<span onClick={() => toggleTodo(todo.id)}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
}
测试 Hooksimport { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';
test('useCounter hook', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
常见陷阱和解决方案1. 闭包问题// ❌ 错误:闭包导致状态更新异常
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // 这里总是使用初始值
}, 1000);
return () => clearInterval(timer);
}, []); // 空依赖数组导致闭包
return <div>计数:{count}</div>;
}
// ✅ 正确:使用函数式更新
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prevCount => prevCount + 1); // 使用函数式更新
}, 1000);
return () => clearInterval(timer);
}, []);
return <div>计数:{count}</div>;
}
2. 依赖数组遗漏// ❌ 错误:遗漏依赖
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, []); // 遗漏 userId 依赖
return <div>{user?.name}</div>;
}
// ✅ 正确:包含所有依赖
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]); // 正确包含依赖
return <div>{user?.name}</div>;
}
性能优化策略1. 合理使用 useMemo 和 useCallback// 只在必要时使用
const expensiveComponent = useMemo(() => {
return <ExpensiveComponent data={data} />;
}, [data]);
// 传递给子组件的回调函数
const handleClick = useCallback(() => {
doSomething(props.a);
}, [props.a]);
2. 使用 React.memo 优化子组件const ExpensiveComponent = React.memo(({ data, onClick }) => {
return (
<div onClick={onClick}>
{data.map(item => (
<Item key={item.id} {...item} />
))}
</div>
);
});
总结React Hooks 为我们提供了强大的工具来管理组件状态和副作用。掌握这些核心概念和最佳实践,将帮助你编写更简洁、可维护的 React 应用。学习路径建议基础阶段:掌握 useState、useEffect、useContext进阶阶段:学习 useReducer、useMemo、useCallback高级阶段:编写自定义 Hooks,优化性能实战阶段:在项目中应用,解决实际问题记住,Hooks 的核心思想是让你能够在函数组件中使用 React 的各种特性。随着经验的积累,你会发现 Hooks 让 React 开发变得更加直观和高效。

发表评论 取消回复