跳到主要内容

React Hooks深度解析与实战指南

React Hooks是React 16.8引入的革命性特性,让函数组件拥有了类组件的所有能力,同时提供了更简洁、更灵活的状态管理和副作用处理方式。通过Hooks,我们可以在函数组件中使用状态、生命周期方法和其他React特性。

核心价值

React Hooks = 状态管理 + 副作用处理 + 逻辑复用 + 性能优化

  • 🎯 状态管理:useState、useReducer提供灵活的状态解决方案
  • 🔄 副作用处理:useEffect统一处理生命周期和副作用
  • 🧩 逻辑复用:自定义Hook实现跨组件逻辑共享
  • 性能优化:useMemo、useCallback、React.memo优化渲染
  • 🎨 代码简洁:函数式编程,减少样板代码
  • 🔧 开发体验:更好的TypeScript支持和调试体验

1. useState - 状态管理基石

useState是最基础也是最重要的Hook,用于在函数组件中添加状态。

useState基础与进阶用法
typescript
1import React, { useState, useCallback } from 'react';
2
3// 1. 基础计数器示例
4const Counter: React.FC = () => {
5 const [count, setCount] = useState(0);
6
7 return (
8 <div className="counter">
9 <h2>计数器: {count}</h2>
10 <button onClick={() => setCount(count + 1)}>+1</button>
11 <button onClick={() => setCount(count - 1)}>-1</button>
12 <button onClick={() => setCount(0)}>重置</button>
13 </div>
14 );
15};
16
17// 2. 复杂状态管理
18interface UserProfile {
19 name: string;
20 email: string;
21 age: number;
22 preferences: {
23 theme: 'light' | 'dark';
24 language: string;
25 notifications: boolean;
26 };
27}
28
29const UserProfileForm: React.FC = () => {
30 const [profile, setProfile] = useState<UserProfile>({
31 name: '',
32 email: '',
33 age: 0,
34 preferences: {
35 theme: 'light',
36 language: 'zh-CN',
37 notifications: true
38 }
39 });
40
41 const updateBasicInfo = useCallback((field: keyof Pick<UserProfile, 'name' | 'email' | 'age'>, value: string | number) => {
42 setProfile(prev => ({
43 ...prev,
44 [field]: value
45 }));
46 }, []);
47
48 const updatePreferences = useCallback((field: keyof UserProfile['preferences'], value: any) => {
49 setProfile(prev => ({
50 ...prev,
51 preferences: {
52 ...prev.preferences,
53 [field]: value
54 }
55 }));
56 }, []);
57
58 return (
59 <form className="user-profile-form">
60 <div className="form-section">
61 <h3>基本信息</h3>
62 <input
63 type="text"
64 placeholder="姓名"
65 value={profile.name}
66 onChange={(e) => updateBasicInfo('name', e.target.value)}
67 />
68 <input
69 type="email"
70 placeholder="邮箱"
71 value={profile.email}
72 onChange={(e) => updateBasicInfo('email', e.target.value)}
73 />
74 <input
75 type="number"
76 placeholder="年龄"
77 value={profile.age}
78 onChange={(e) => updateBasicInfo('age', parseInt(e.target.value) || 0)}
79 />
80 </div>
81
82 <div className="form-section">
83 <h3>偏好设置</h3>
84 <select
85 value={profile.preferences.theme}
86 onChange={(e) => updatePreferences('theme', e.target.value)}
87 >
88 <option value="light">浅色主题</option>
89 <option value="dark">深色主题</option>
90 </select>
91
92 <select
93 value={profile.preferences.language}
94 onChange={(e) => updatePreferences('language', e.target.value)}
95 >
96 <option value="zh-CN">中文</option>
97 <option value="en-US">English</option>
98 </select>
99
100 <label>
101 <input
102 type="checkbox"
103 checked={profile.preferences.notifications}
104 onChange={(e) => updatePreferences('notifications', e.target.checked)}
105 />
106 接收通知
107 </label>
108 </div>
109 </form>
110 );
111};

2. useEffect - 副作用管理专家

useEffect是处理副作用的核心Hook,统一了类组件中的componentDidMount、componentDidUpdate和componentWillUnmount。

useEffect基础用法
typescript
1import React, { useState, useEffect, useCallback } from 'react';
2
3// 1. 基础副作用处理
4const BasicEffectExample: React.FC = () => {
5 const [count, setCount] = useState(0);
6 const [title, setTitle] = useState('React App');
7
8 // 无依赖数组 - 每次渲染后都执行
9 useEffect(() => {
10 console.log('组件渲染完成');
11 });
12
13 // 空依赖数组 - 只在挂载时执行一次
14 useEffect(() => {
15 console.log('组件已挂载');
16 document.title = title;
17
18 return () => {
19 console.log('组件即将卸载');
20 document.title = 'React App';
21 };
22 }, []);
23
24 // 有依赖数组 - 依赖变化时执行
25 useEffect(() => {
26 document.title = `计数: ${count}`;
27 }, [count]);
28
29 return (
30 <div className="basic-effect-example">
31 <h2>{title}</h2>
32 <p>计数: {count}</p>
33 <button onClick={() => setCount(count + 1)}>增加</button>
34 <input
35 type="text"
36 value={title}
37 onChange={(e) => setTitle(e.target.value)}
38 placeholder="修改标题"
39 />
40 </div>
41 );
42};
43
44// 2. 数据获取副作用
45interface User {
46 id: number;
47 name: string;
48 email: string;
49}
50
51const DataFetchingExample: React.FC<{ userId: number }> = ({ userId }) => {
52 const [user, setUser] = useState<User | null>(null);
53 const [loading, setLoading] = useState(true);
54 const [error, setError] = useState<string | null>(null);
55
56 useEffect(() => {
57 let cancelled = false;
58
59 const fetchUser = async () => {
60 try {
61 setLoading(true);
62 setError(null);
63
64 const response = await fetch(`/api/users/${userId}`);
65 if (!response.ok) {
66 throw new Error('获取用户信息失败');
67 }
68
69 const userData = await response.json();
70
71 if (!cancelled) {
72 setUser(userData);
73 }
74 } catch (err) {
75 if (!cancelled) {
76 setError(err instanceof Error ? err.message : '未知错误');
77 }
78 } finally {
79 if (!cancelled) {
80 setLoading(false);
81 }
82 }
83 };
84
85 fetchUser();
86
87 return () => {
88 cancelled = true;
89 };
90 }, [userId]);
91
92 if (loading) return <div>加载中...</div>;
93 if (error) return <div>错误: {error}</div>;
94 if (!user) return <div>用户不存在</div>;
95
96 return (
97 <div className="user-info">
98 <h3>{user.name}</h3>
99 <p>邮箱: {user.email}</p>
100 </div>
101 );
102};

3. useContext - 跨组件状态共享

useContext提供了一种在组件树中传递数据的方法,避免了props drilling问题。

useContext实现
typescript
1import React, { createContext, useContext, useState, ReactNode } from 'react';
2
3// 1. 主题Context
4interface ThemeContextType {
5 theme: 'light' | 'dark';
6 toggleTheme: () => void;
7}
8
9const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
10
11export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
12 const [theme, setTheme] = useState<'light' | 'dark'>('light');
13
14 const toggleTheme = () => {
15 setTheme(prev => prev === 'light' ? 'dark' : 'light');
16 };
17
18 const value = {
19 theme,
20 toggleTheme
21 };
22
23 return (
24 <ThemeContext.Provider value={value}>
25 {children}
26 </ThemeContext.Provider>
27 );
28};
29
30export const useTheme = () => {
31 const context = useContext(ThemeContext);
32 if (context === undefined) {
33 throw new Error('useTheme must be used within a ThemeProvider');
34 }
35 return context;
36};
37
38// 使用示例
39const ThemeToggle: React.FC = () => {
40 const { theme, toggleTheme } = useTheme();
41
42 return (
43 <button onClick={toggleTheme}>
44 切换到 {theme === 'light' ? '深色' : '浅色'} 主题
45 </button>
46 );
47};

4. 自定义Hooks - 逻辑复用的艺术

自定义Hook是React中实现逻辑复用的强大工具。

自定义Hook最佳实践
typescript
1// 1. 数据获取Hook
2function useApi<T>(url: string) {
3 const [data, setData] = useState<T | null>(null);
4 const [loading, setLoading] = useState(true);
5 const [error, setError] = useState<string | null>(null);
6
7 useEffect(() => {
8 const fetchData = async () => {
9 try {
10 setLoading(true);
11 setError(null);
12
13 const response = await fetch(url);
14 if (!response.ok) {
15 throw new Error(`HTTP error! status: ${response.status}`);
16 }
17
18 const result = await response.json();
19 setData(result);
20 } catch (err) {
21 setError(err instanceof Error ? err.message : 'An error occurred');
22 } finally {
23 setLoading(false);
24 }
25 };
26
27 fetchData();
28 }, [url]);
29
30 return { data, loading, error };
31}
32
33// 2. 本地存储Hook
34function useLocalStorage<T>(key: string, initialValue: T) {
35 const [storedValue, setStoredValue] = useState<T>(() => {
36 try {
37 const item = window.localStorage.getItem(key);
38 return item ? JSON.parse(item) : initialValue;
39 } catch (error) {
40 console.error(`Error reading localStorage key "${key}":`, error);
41 return initialValue;
42 }
43 });
44
45 const setValue = useCallback((value: T | ((val: T) => T)) => {
46 try {
47 const valueToStore = value instanceof Function ? value(storedValue) : value;
48 setStoredValue(valueToStore);
49 window.localStorage.setItem(key, JSON.stringify(valueToStore));
50 } catch (error) {
51 console.error(`Error setting localStorage key "${key}":`, error);
52 }
53 }, [key, storedValue]);
54
55 return [storedValue, setValue] as const;
56}
57
58// 使用示例
59const UserProfile: React.FC = () => {
60 const { data: user, loading, error } = useApi<User>('/api/user/profile');
61 const [preferences, setPreferences] = useLocalStorage('userPreferences', {
62 theme: 'light',
63 language: 'en'
64 });
65
66 if (loading) return <div>Loading...</div>;
67 if (error) return <div>Error: {error}</div>;
68
69 return (
70 <div>
71 <h1>{user?.name}</h1>
72 <p>Theme: {preferences.theme}</p>
73 <button onClick={() => setPreferences(prev => ({
74 ...prev,
75 theme: prev.theme === 'light' ? 'dark' : 'light'
76 }))}>
77 Toggle Theme
78 </button>
79 </div>
80 );
81};

5. 性能优化Hooks

React提供了多个用于性能优化的Hooks。

useMemo优化昂贵计算
typescript
1import React, { useState, useMemo } from 'react';
2
3const ExpensiveCalculation: React.FC = () => {
4 const [numbers, setNumbers] = useState<number[]>([]);
5 const [filter, setFilter] = useState('');
6
7 // 使用useMemo缓存计算结果
8 const expensiveResult = useMemo(() => {
9 console.log('执行昂贵计算');
10 return numbers
11 .filter(n => n.toString().includes(filter))
12 .reduce((sum, n) => sum + n, 0);
13 }, [numbers, filter]);
14
15 return (
16 <div>
17 <input
18 type="text"
19 value={filter}
20 onChange={(e) => setFilter(e.target.value)}
21 placeholder="过滤数字..."
22 />
23 <p>计算结果: {expensiveResult}</p>
24 <button onClick={() => setNumbers(prev => [...prev, Math.random() * 100])}>
25 添加随机数
26 </button>
27 </div>
28 );
29};

通过掌握这些React Hooks的核心概念和最佳实践,你可以构建更加高效、可维护的React应用程序。

评论