难度:中级
考察要点:
- 自定义 Hook 的设计原则
- 状态复用与逻辑抽象
- TypeScript 类型定义
- 性能优化考虑
解答:
1. 概念解释
基本定义:
- 自定义 Hook 是一种复用状态逻辑的函数
- 必须以 "use" 开头命名
- 可以调用其他 Hooks
核心原理:
- 遵循 Hooks 的规则
- 在不同组件间共享逻辑,但状态是独立的
- 可以返回任意值,不限于状态
使用场景:
- 抽象复杂的状态逻辑
- 处理副作用
- 封装通用功能
2. 代码示例
基础示例:useLocalStorage
import { useState, useEffect } from 'react';
interface UseLocalStorageOptions<T> {
serializer?: (value: T) => string;
deserializer?: (value: string) => T;
}
export function useLocalStorage<T>(
key: string,
initialValue: T,
options: UseLocalStorageOptions<T> = {}
) {
// 自定义序列化和反序列化方法
const {
serializer = JSON.stringify,
deserializer = JSON.parse
} = options;
// 惰性初始化状态
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? deserializer(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
// 同步到 localStorage
useEffect(() => {
try {
window.localStorage.setItem(key, serializer(storedValue));
} catch (error) {
console.error(error);
}
}, [key, storedValue, serializer]);
return [storedValue, setStoredValue] as const;
}
进阶示例:useAsync
import { useState, useCallback } from 'react';
interface AsyncState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
export function useAsync<T>() {
const [state, setState] = useState<AsyncState<T>>({
data: null,
loading: false,
error: null
});
const execute = useCallback(async (asyncFunction: () => Promise<T>) => {
setState(prev => ({ ...prev, loading: true }));
try {
const data = await asyncFunction();
setState({ data, loading: false, error: null });
return data;
} catch (error) {
setState({ data: null, loading: false, error: error as Error });
throw error;
}
}, []);
return { ...state, execute };
}
最佳实践示例:
import { useLocalStorage, useAsync } from '../hooks';
function UserProfile() {
// 使用自定义 Hook 管理用户设置
const [settings, setSettings] = useLocalStorage('user-settings', {
theme: 'light',
notifications: true
});
// 使用异步 Hook 处理数据获取
const { data: user, loading, error, execute } = useAsync<User>();
useEffect(() => {
execute(() => fetchUserProfile(userId));
}, [execute, userId]);
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
if (!user) return null;
return (
<div>
<h1>{user.name}</h1>
<ThemeToggle
value={settings.theme}
onChange={theme => setSettings({ ...settings, theme })}
/>
</div>
);
}
3. 注意事项
常见陷阱:
- 避免在循环或条件语句中使用
- 保持 Hook 的纯函数特性
- 正确处理清理函数
性能考虑:
export function useOptimizedHook(value: string) {
// 使用 useMemo 缓存计算结果
const processedValue = useMemo(() => {
return expensiveOperation(value);
}, [value]);
// 使用 useCallback 缓存函数
const handleChange = useCallback((newValue: string) => {
// 处理逻辑
}, []);
return { processedValue, handleChange };
}
兼容性问题:
- 考虑浏览器 API 的兼容性
- 提供降级方案
- 处理服务器端渲染
4. 扩展知识
相关概念:
- Hook 组合
- 高阶组件 vs Hooks
- 状态管理模式
替代方案:
// 对于复杂状态,考虑使用 useReducer
export function useComplexState() {
const [state, dispatch] = useReducer(reducer, initialState);
const actions = useMemo(() => ({
action1: () => dispatch({ type: 'ACTION_1' }),
action2: (payload) => dispatch({ type: 'ACTION_2', payload })
}), []);
return [state, actions] as const;
}
实际应用:
- 表单处理
- 数据获取
- 动画控制
- 状态持久化
- 实时数据同步
5. 测试策略
import { renderHook, act } from '@testing-library/react-hooks';
import { useLocalStorage } from '../useLocalStorage';
describe('useLocalStorage', () => {
beforeEach(() => {
window.localStorage.clear();
});
it('should store and retrieve values', () => {
const { result } = renderHook(() =>
useLocalStorage('test-key', 'initial')
);
act(() => {
result.current[1]('new value');
});
expect(result.current[0]).toBe('new value');
expect(window.localStorage.getItem('test-key')).toBe('"new value"');
});
});
自定义 Hook 是 React 中重要的代码复用机制,掌握其设计模式和最佳实践对于构建可维护的 React 应用至关重要。通过合理的抽象和组合,可以显著提高代码的可复用性和可测试性。