Appearance
Context与Hooks结合
概述
Context API与Hooks的结合是React现代状态管理的核心模式。本文深入探讨如何有效地将Context与各种Hooks结合使用,构建强大而灵活的状态管理方案。
基础结合模式
Context + useState
最基本的组合,适合简单状态管理:
jsx
const CountContext = createContext();
function CountProvider({ children }) {
const [count, setCount] = useState(0);
const value = useMemo(() => ({
count,
setCount,
increment: () => setCount(c => c + 1),
decrement: () => setCount(c => c - 1),
reset: () => setCount(0)
}), [count]);
return (
<CountContext.Provider value={value}>
{children}
</CountContext.Provider>
);
}
function useCount() {
const context = useContext(CountContext);
if (!context) {
throw new Error('useCount must be used within CountProvider');
}
return context;
}
// 使用
function Counter() {
const { count, increment, decrement, reset } = useCount();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}Context + useReducer
适合复杂状态逻辑:
jsx
// 1. 定义reducer
const todoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, { id: Date.now(), text: action.payload, completed: false }]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
)
};
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case 'SET_FILTER':
return { ...state, filter: action.payload };
default:
return state;
}
};
// 2. 创建Context
const TodoStateContext = createContext();
const TodoDispatchContext = createContext();
// 3. Provider组件
function TodoProvider({ children }) {
const [state, dispatch] = useReducer(todoReducer, {
todos: [],
filter: 'all'
});
return (
<TodoStateContext.Provider value={state}>
<TodoDispatchContext.Provider value={dispatch}>
{children}
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
);
}
// 4. 自定义Hooks
function useTodoState() {
const context = useContext(TodoStateContext);
if (!context) {
throw new Error('useTodoState must be used within TodoProvider');
}
return context;
}
function useTodoDispatch() {
const context = useContext(TodoDispatchContext);
if (!context) {
throw new Error('useTodoDispatch must be used within TodoProvider');
}
return context;
}
// 5. 高级Hook - 提供action creators
function useTodoActions() {
const dispatch = useTodoDispatch();
return useMemo(() => ({
addTodo: (text) => dispatch({ type: 'ADD_TODO', payload: text }),
toggleTodo: (id) => dispatch({ type: 'TOGGLE_TODO', payload: id }),
deleteTodo: (id) => dispatch({ type: 'DELETE_TODO', payload: id }),
setFilter: (filter) => dispatch({ type: 'SET_FILTER', payload: filter })
}), [dispatch]);
}
// 使用
function TodoList() {
const { todos, filter } = useTodoState();
const { toggleTodo, deleteTodo } = useTodoActions();
const filteredTodos = useMemo(() => {
switch (filter) {
case 'active': return todos.filter(t => !t.completed);
case 'completed': return todos.filter(t => t.completed);
default: return todos;
}
}, [todos, filter]);
return (
<ul>
{filteredTodos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
);
}Context + useEffect
处理副作用和数据同步:
jsx
const DataContext = createContext();
function DataProvider({ children }) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// 初始化时加载数据
useEffect(() => {
setLoading(true);
fetchData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
// 数据变化时同步到服务器
useEffect(() => {
if (data.length > 0) {
syncDataToServer(data).catch(console.error);
}
}, [data]);
// 监听在线状态
useEffect(() => {
const handleOnline = () => {
syncDataToServer(data);
};
window.addEventListener('online', handleOnline);
return () => window.removeEventListener('online', handleOnline);
}, [data]);
const value = useMemo(
() => ({
data,
setData,
loading,
error,
refresh: () => {
setLoading(true);
fetchData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}
}),
[data, loading, error]
);
return <DataContext.Provider value={value}>{children}</DataContext.Provider>;
}Context + useMemo
优化计算密集型操作:
jsx
const DataContext = createContext();
function DataProvider({ children }) {
const [rawData, setRawData] = useState([]);
const [filters, setFilters] = useState({});
const [sortConfig, setSortConfig] = useState({ key: 'date', direction: 'desc' });
// 缓存过滤逻辑
const filteredData = useMemo(() => {
console.log('Computing filtered data...');
let result = rawData;
Object.entries(filters).forEach(([key, value]) => {
if (value) {
result = result.filter(item => item[key] === value);
}
});
return result;
}, [rawData, filters]);
// 缓存排序逻辑
const sortedData = useMemo(() => {
console.log('Computing sorted data...');
const { key, direction } = sortConfig;
const sorted = [...filteredData];
sorted.sort((a, b) => {
if (a[key] < b[key]) return direction === 'asc' ? -1 : 1;
if (a[key] > b[key]) return direction === 'asc' ? 1 : -1;
return 0;
});
return sorted;
}, [filteredData, sortConfig]);
// 缓存统计信息
const statistics = useMemo(() => {
console.log('Computing statistics...');
return {
total: rawData.length,
filtered: filteredData.length,
sorted: sortedData.length,
categories: [...new Set(rawData.map(item => item.category))]
};
}, [rawData, filteredData, sortedData]);
const value = useMemo(
() => ({
rawData,
setRawData,
filteredData,
sortedData,
statistics,
filters,
setFilters,
sortConfig,
setSortConfig
}),
[rawData, filteredData, sortedData, statistics, filters, sortConfig]
);
return <DataContext.Provider value={value}>{children}</DataContext.Provider>;
}Context + useCallback
稳定函数引用,避免不必要的重渲染:
jsx
const ActionsContext = createContext();
function ActionsProvider({ children }) {
const [items, setItems] = useState([]);
// 使用useCallback确保函数引用稳定
const addItem = useCallback((item) => {
setItems(prev => [...prev, { ...item, id: Date.now() }]);
}, []);
const removeItem = useCallback((id) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []);
const updateItem = useCallback((id, updates) => {
setItems(prev =>
prev.map(item => (item.id === id ? { ...item, ...updates } : item))
);
}, []);
const clearItems = useCallback(() => {
setItems([]);
}, []);
// 批量操作
const batchUpdate = useCallback((operations) => {
setItems(prev => {
let result = prev;
operations.forEach(op => {
switch (op.type) {
case 'add':
result = [...result, { ...op.item, id: Date.now() }];
break;
case 'remove':
result = result.filter(item => item.id !== op.id);
break;
case 'update':
result = result.map(item =>
item.id === op.id ? { ...item, ...op.updates } : item
);
break;
}
});
return result;
});
}, []);
const value = useMemo(
() => ({
items,
addItem,
removeItem,
updateItem,
clearItems,
batchUpdate
}),
[items, addItem, removeItem, updateItem, clearItems, batchUpdate]
);
return <ActionsContext.Provider value={value}>{children}</ActionsContext.Provider>;
}
// 使用React.memo的组件只在props变化时重渲染
const ItemComponent = React.memo(({ item, onRemove }) => {
console.log('ItemComponent render:', item.id);
return (
<div>
<span>{item.name}</span>
<button onClick={() => onRemove(item.id)}>Remove</button>
</div>
);
});
function ItemList() {
const { items, removeItem } = useContext(ActionsContext);
// removeItem引用稳定,ItemComponent不会因此重渲染
return (
<div>
{items.map(item => (
<ItemComponent key={item.id} item={item} onRemove={removeItem} />
))}
</div>
);
}高级结合模式
Context + useRef
保存可变值而不触发重渲染:
jsx
const TimerContext = createContext();
function TimerProvider({ children }) {
const [time, setTime] = useState(0);
const [isRunning, setIsRunning] = useState(false);
const intervalRef = useRef(null);
const startTimeRef = useRef(null);
const start = useCallback(() => {
if (!isRunning) {
startTimeRef.current = Date.now() - time;
intervalRef.current = setInterval(() => {
setTime(Date.now() - startTimeRef.current);
}, 10);
setIsRunning(true);
}
}, [isRunning, time]);
const pause = useCallback(() => {
if (isRunning) {
clearInterval(intervalRef.current);
setIsRunning(false);
}
}, [isRunning]);
const reset = useCallback(() => {
clearInterval(intervalRef.current);
setTime(0);
setIsRunning(false);
}, []);
// 清理定时器
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
const value = useMemo(
() => ({
time,
isRunning,
start,
pause,
reset
}),
[time, isRunning, start, pause, reset]
);
return <TimerContext.Provider value={value}>{children}</TimerContext.Provider>;
}
function Timer() {
const { time, isRunning, start, pause, reset } = useContext(TimerContext);
const formatTime = (ms) => {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
return `${hours.toString().padStart(2, '0')}:${(minutes % 60)
.toString()
.padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
};
return (
<div>
<h2>{formatTime(time)}</h2>
<button onClick={start} disabled={isRunning}>Start</button>
<button onClick={pause} disabled={!isRunning}>Pause</button>
<button onClick={reset}>Reset</button>
</div>
);
}Context + useLayoutEffect
同步DOM操作:
jsx
const LayoutContext = createContext();
function LayoutProvider({ children }) {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const containerRef = useRef(null);
useLayoutEffect(() => {
if (containerRef.current) {
const { width, height } = containerRef.current.getBoundingClientRect();
setDimensions({ width, height });
}
}, []);
useLayoutEffect(() => {
const handleResize = () => {
if (containerRef.current) {
const { width, height } = containerRef.current.getBoundingClientRect();
setDimensions({ width, height });
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const value = useMemo(
() => ({
dimensions,
containerRef
}),
[dimensions]
);
return <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>;
}
function ResponsiveComponent() {
const { dimensions, containerRef } = useContext(LayoutContext);
return (
<div ref={containerRef}>
<p>Width: {dimensions.width}px</p>
<p>Height: {dimensions.height}px</p>
</div>
);
}Context + useTransition
处理低优先级更新:
jsx
import { useTransition } from 'react';
const SearchContext = createContext();
function SearchProvider({ children }) {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const search = useCallback((searchQuery) => {
setQuery(searchQuery);
// 将结果更新标记为低优先级
startTransition(() => {
const filtered = largeDataset.filter(item =>
item.name.toLowerCase().includes(searchQuery.toLowerCase())
);
setResults(filtered);
});
}, []);
const value = useMemo(
() => ({
query,
results,
isPending,
search
}),
[query, results, isPending, search]
);
return <SearchContext.Provider value={value}>{children}</SearchContext.Provider>;
}
function SearchInput() {
const { query, search, isPending } = useContext(SearchContext);
return (
<div>
<input
type="text"
value={query}
onChange={(e) => search(e.target.value)}
placeholder="Search..."
/>
{isPending && <span className="loading">Searching...</span>}
</div>
);
}
function SearchResults() {
const { results } = useContext(SearchContext);
return (
<ul>
{results.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}Context + useSyncExternalStore
同步外部状态:
jsx
import { useSyncExternalStore } from 'react';
// 外部存储
class ExternalStore {
constructor() {
this.state = { count: 0 };
this.listeners = new Set();
}
subscribe = (listener) => {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
};
getSnapshot = () => {
return this.state;
};
setState = (newState) => {
this.state = { ...this.state, ...newState };
this.listeners.forEach(listener => listener());
};
increment = () => {
this.setState({ count: this.state.count + 1 });
};
}
const externalStore = new ExternalStore();
const ExternalStoreContext = createContext(externalStore);
function ExternalStoreProvider({ children }) {
return (
<ExternalStoreContext.Provider value={externalStore}>
{children}
</ExternalStoreContext.Provider>
);
}
function useExternalStore() {
const store = useContext(ExternalStoreContext);
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
return {
state,
increment: store.increment
};
}
function ExternalStoreComponent() {
const { state, increment } = useExternalStore();
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}自定义Hooks模式
模式1:封装Context逻辑
jsx
// 创建完整的Context模块
function createContextModule(useValue, displayName) {
const Context = createContext(undefined);
Context.displayName = displayName;
function Provider({ children }) {
const value = useValue();
return <Context.Provider value={value}>{children}</Context.Provider>;
}
function useContextValue() {
const context = useContext(Context);
if (context === undefined) {
throw new Error(`useContextValue must be used within ${displayName}Provider`);
}
return context;
}
return [Provider, useContextValue];
}
// 使用
const [CountProvider, useCount] = createContextModule(() => {
const [count, setCount] = useState(0);
return useMemo(
() => ({
count,
increment: () => setCount(c => c + 1),
decrement: () => setCount(c => c - 1)
}),
[count]
);
}, 'Count');模式2:组合多个Hooks
jsx
const UserContext = createContext();
function UserProvider({ children }) {
// 组合多个Hooks
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// 认证逻辑
const login = useCallback(async (credentials) => {
setLoading(true);
setError(null);
try {
const userData = await authAPI.login(credentials);
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(async () => {
setLoading(true);
try {
await authAPI.logout();
setUser(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, []);
// 持久化
useEffect(() => {
const savedUser = localStorage.getItem('user');
if (savedUser) {
setUser(JSON.parse(savedUser));
}
}, []);
useEffect(() => {
if (user) {
localStorage.setItem('user', JSON.stringify(user));
} else {
localStorage.removeItem('user');
}
}, [user]);
// 自动刷新token
useEffect(() => {
if (!user) return;
const interval = setInterval(async () => {
try {
const refreshedUser = await authAPI.refreshToken();
setUser(refreshedUser);
} catch (err) {
setError(err.message);
setUser(null);
}
}, 15 * 60 * 1000); // 15分钟
return () => clearInterval(interval);
}, [user]);
const value = useMemo(
() => ({
user,
loading,
error,
login,
logout,
isAuthenticated: !!user
}),
[user, loading, error, login, logout]
);
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
}模式3:条件Hook执行
jsx
const FeatureContext = createContext();
function FeatureProvider({ children }) {
const [features, setFeatures] = useState({
analytics: false,
experiments: false
});
// 条件执行Hook
useEffect(() => {
if (features.analytics) {
// 初始化分析工具
initAnalytics();
return () => cleanupAnalytics();
}
}, [features.analytics]);
useEffect(() => {
if (features.experiments) {
// 初始化实验工具
initExperiments();
return () => cleanupExperiments();
}
}, [features.experiments]);
const value = useMemo(
() => ({
features,
setFeatures,
enableFeature: (name) => setFeatures(prev => ({ ...prev, [name]: true })),
disableFeature: (name) => setFeatures(prev => ({ ...prev, [name]: false }))
}),
[features]
);
return <FeatureContext.Provider value={value}>{children}</FeatureContext.Provider>;
}实战案例
案例1:表单管理Context
jsx
const FormContext = createContext();
function FormProvider({ initialValues = {}, onSubmit, children }) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
// 字段值更新
const setFieldValue = useCallback((name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
}, []);
// 字段错误设置
const setFieldError = useCallback((name, error) => {
setErrors(prev => ({ ...prev, [name]: error }));
}, []);
// 字段触摸状态
const setFieldTouched = useCallback((name, isTouched = true) => {
setTouched(prev => ({ ...prev, [name]: isTouched }));
}, []);
// 验证字段
const validateField = useCallback((name, value, validator) => {
if (validator) {
const error = validator(value);
setFieldError(name, error);
return !error;
}
return true;
}, [setFieldError]);
// 提交表单
const handleSubmit = useCallback(async (e) => {
e?.preventDefault();
// 标记所有字段为已触摸
const allTouched = Object.keys(values).reduce(
(acc, key) => ({ ...acc, [key]: true }),
{}
);
setTouched(allTouched);
// 检查错误
const hasErrors = Object.values(errors).some(error => error);
if (hasErrors) return;
setIsSubmitting(true);
try {
await onSubmit(values);
} catch (err) {
console.error('Form submission error:', err);
} finally {
setIsSubmitting(false);
}
}, [values, errors, onSubmit]);
// 重置表单
const resetForm = useCallback(() => {
setValues(initialValues);
setErrors({});
setTouched({});
setIsSubmitting(false);
}, [initialValues]);
const value = useMemo(
() => ({
values,
errors,
touched,
isSubmitting,
setFieldValue,
setFieldError,
setFieldTouched,
validateField,
handleSubmit,
resetForm
}),
[
values,
errors,
touched,
isSubmitting,
setFieldValue,
setFieldError,
setFieldTouched,
validateField,
handleSubmit,
resetForm
]
);
return <FormContext.Provider value={value}>{children}</FormContext.Provider>;
}
function useFormContext() {
const context = useContext(FormContext);
if (!context) {
throw new Error('useFormContext must be used within FormProvider');
}
return context;
}
// 自定义字段Hook
function useField(name, validator) {
const {
values,
errors,
touched,
setFieldValue,
setFieldTouched,
validateField
} = useFormContext();
const value = values[name] || '';
const error = errors[name];
const isTouched = touched[name];
const handleChange = useCallback((e) => {
const newValue = e.target.value;
setFieldValue(name, newValue);
validateField(name, newValue, validator);
}, [name, setFieldValue, validateField, validator]);
const handleBlur = useCallback(() => {
setFieldTouched(name, true);
validateField(name, value, validator);
}, [name, value, setFieldTouched, validateField, validator]);
return {
value,
error,
isTouched,
onChange: handleChange,
onBlur: handleBlur
};
}
// 使用示例
function LoginForm() {
const handleSubmit = async (values) => {
console.log('Submitting:', values);
await login(values);
};
return (
<FormProvider
initialValues={{ email: '', password: '' }}
onSubmit={handleSubmit}
>
<FormContent />
</FormProvider>
);
}
function FormContent() {
const { handleSubmit, isSubmitting } = useFormContext();
const emailField = useField('email', (value) => {
if (!value) return 'Email is required';
if (!/\S+@\S+\.\S+/.test(value)) return 'Invalid email';
return null;
});
const passwordField = useField('password', (value) => {
if (!value) return 'Password is required';
if (value.length < 6) return 'Password must be at least 6 characters';
return null;
});
return (
<form onSubmit={handleSubmit}>
<div>
<input {...emailField} placeholder="Email" />
{emailField.isTouched && emailField.error && (
<span className="error">{emailField.error}</span>
)}
</div>
<div>
<input {...passwordField} type="password" placeholder="Password" />
{passwordField.isTouched && passwordField.error && (
<span className="error">{passwordField.error}</span>
)}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Logging in...' : 'Login'}
</button>
</form>
);
}案例2:通知系统Context
jsx
const NotificationContext = createContext();
function NotificationProvider({ children }) {
const [notifications, setNotifications] = useState([]);
const timeoutRefs = useRef(new Map());
// 添加通知
const addNotification = useCallback((message, type = 'info', duration = 3000) => {
const id = Date.now();
const notification = { id, message, type };
setNotifications(prev => [...prev, notification]);
if (duration > 0) {
const timeoutId = setTimeout(() => {
removeNotification(id);
}, duration);
timeoutRefs.current.set(id, timeoutId);
}
return id;
}, []);
// 移除通知
const removeNotification = useCallback((id) => {
setNotifications(prev => prev.filter(n => n.id !== id));
const timeoutId = timeoutRefs.current.get(id);
if (timeoutId) {
clearTimeout(timeoutId);
timeoutRefs.current.delete(id);
}
}, []);
// 清理所有定时器
useEffect(() => {
return () => {
timeoutRefs.current.forEach(timeoutId => clearTimeout(timeoutId));
timeoutRefs.current.clear();
};
}, []);
// 便捷方法
const success = useCallback((message, duration) => {
return addNotification(message, 'success', duration);
}, [addNotification]);
const error = useCallback((message, duration) => {
return addNotification(message, 'error', duration);
}, [addNotification]);
const warning = useCallback((message, duration) => {
return addNotification(message, 'warning', duration);
}, [addNotification]);
const info = useCallback((message, duration) => {
return addNotification(message, 'info', duration);
}, [addNotification]);
const value = useMemo(
() => ({
notifications,
addNotification,
removeNotification,
success,
error,
warning,
info
}),
[notifications, addNotification, removeNotification, success, error, warning, info]
);
return (
<NotificationContext.Provider value={value}>
{children}
<NotificationContainer />
</NotificationContext.Provider>
);
}
function NotificationContainer() {
const { notifications, removeNotification } = useContext(NotificationContext);
return (
<div className="notification-container">
{notifications.map(notification => (
<div key={notification.id} className={`notification ${notification.type}`}>
<span>{notification.message}</span>
<button onClick={() => removeNotification(notification.id)}>×</button>
</div>
))}
</div>
);
}
function useNotifications() {
const context = useContext(NotificationContext);
if (!context) {
throw new Error('useNotifications must be used within NotificationProvider');
}
return context;
}
// 使用
function MyComponent() {
const { success, error } = useNotifications();
const handleSave = async () => {
try {
await saveData();
success('Data saved successfully!');
} catch (err) {
error('Failed to save data');
}
};
return <button onClick={handleSave}>Save</button>;
}最佳实践总结
1. Hook选择指南
jsx
// 简单状态 -> useState
const [count, setCount] = useState(0);
// 复杂状态逻辑 -> useReducer
const [state, dispatch] = useReducer(reducer, initialState);
// 副作用 -> useEffect
useEffect(() => {
// 副作用代码
}, [dependencies]);
// 计算缓存 -> useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
// 函数缓存 -> useCallback
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
// 可变值 -> useRef
const mutableValue = useRef(initialValue);2. Context + Hooks结合原则
- 分离关注点:State和Dispatch使用不同Context
- 稳定引用:使用useMemo和useCallback
- 避免过度优化:先确保正确性,再优化性能
- 清理副作用:useEffect返回清理函数
- 错误处理:提供清晰的错误信息
3. 性能优化检查清单
- [ ] Context value使用useMemo包装
- [ ] 回调函数使用useCallback包装
- [ ] 计算属性使用useMemo缓存
- [ ] 分离读写Context
- [ ] 合理使用React.memo
- [ ] 避免在render中创建新对象/函数
总结
Context与Hooks的结合是React状态管理的核心。关键要点:
- useState:简单状态管理
- useReducer:复杂状态逻辑
- useEffect:副作用处理
- useMemo:计算缓存
- useCallback:函数缓存
- useRef:可变值引用
- 自定义Hooks:封装和复用逻辑
正确使用这些Hooks,可以构建强大、灵活、高性能的状态管理方案。