02-Hooks完全指南——12-Hooks 最佳实践总结

📅 2026/6/18 21:43:05 👤 管理员 👁 次浏览
02-Hooks完全指南——12-Hooks 最佳实践总结
Hooks 最佳实践总结一、Hooks 设计原则1.1 单一职责每个 Hook 应该只做一件事// ❌ 不好的设计一个 Hook 做太多事 function useUserAndPosts(userId) { const [user, setUser] useState(null); const [posts, setPosts] useState([]); // ... return { user, posts }; } // ✅ 好的设计拆分为两个 Hook function useUser(userId) { ... } function usePosts(userId) { ... } // 在组件中组合 const user useUser(userId); const posts usePosts(userId);1.2 可组合性// 自定义 Hook 可以组合其他 Hooks function useUserProfile(userId) { const user useUser(userId); const posts usePosts(userId); const isOnline useOnlineStatus(userId); return { user, posts, isOnline }; }1.3 可测试性// 独立的 Hook 更容易测试 function useCounter(initial 0) { const [count, setCount] useState(initial); const increment useCallback(() setCount(prev prev 1), []); const decrement useCallback(() setCount(prev prev - 1), []); const reset useCallback(() setCount(initial), [initial]); return { count, increment, decrement, reset }; } // 测试 it(should increment counter, () { const { result } renderHook(() useCounter(0)); act(() result.current.increment()); expect(result.current.count).toBe(1); });二、Hook 选择指南2.1 状态管理选择场景推荐 Hook原因简单状态布尔、数字useState最简单表单状态useState 对象灵活复杂状态逻辑useReducer清晰跨组件状态Context useReducer全局服务端状态useFetch useReducer统一2.2 副作用处理场景推荐方式数据获取useEffect 清理订阅useEffect 清理函数DOM 操作useRef useEffect布局测量useLayoutEffect2.3 性能优化选择场景推荐方式缓存计算结果useMemo缓存函数useCallback避免子组件重渲染React.memo useCallback延迟更新useTransition防抖/节流useDebounce 自定义 Hook三、项目结构建议3.1 文件夹组织src/ ├── hooks/ │ ├── index.js # 导出所有 hooks │ ├── useAuth.js # 认证相关 │ ├── useFetch.js # 数据获取 │ ├── useLocalStorage.js # 本地存储 │ └── useDebounce.js # 防抖 ├── components/ │ ├── UserProfile/ │ │ ├── UserProfile.js │ │ ├── useUserData.js # 组件专用 Hook │ │ └── UserProfile.css │ └── ... └── ...3.2 通用 Hooks 库// hooks/index.js export { useAuth } from ./useAuth; export { useFetch } from ./useFetch; export { useLocalStorage } from ./useLocalStorage; export { useDebounce } from ./useDebounce; export { useToggle } from ./useToggle; export { usePrevious } from ./usePrevious;四、性能优化策略4.1 减少不必要的渲染// 1. 使用 React.memo 包裹组件 const TodoItem React.memo(({ todo, onToggle }) { return li onClick{() onToggle(todo.id)}{todo.text}/li; }); // 2. 使用 useCallback 稳定回调 const handleToggle useCallback((id) { setTodos(prev prev.map(todo todo.id id ? { ...todo, completed: !todo.completed } : todo )); }, []); // 3. 使用 useMemo 缓存计算结果 const completedCount useMemo(() { return todos.filter(todo todo.completed).length; }, [todos]);4.2 懒加载和代码分割const HeavyComponent lazy(() import(./HeavyComponent)); function App() { const [show, setShow] useState(false); return ( div button onClick{() setShow(true)}加载/button {show ( Suspense fallback{div加载中.../div} HeavyComponent / /Suspense )} /div ); }五、代码质量5.1 TypeScript 支持// 带类型的自定义 Hook function useLocalStorageT(key: string, initialValue: T): [T, (value: T | ((prev: T) T)) void] { const [storedValue, setStoredValue] useStateT(() { try { const item localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { return initialValue; } }); const setValue useCallback((value: T | ((prev: T) T)) { try { const valueToStore value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); localStorage.setItem(key, JSON.stringify(valueToStore)); } catch (error) { console.error(error); } }, [key, storedValue]); return [storedValue, setValue]; }5.2 错误处理function useFetch(url) { const [data, setData] useState(null); const [error, setError] useState(null); const [loading, setLoading] useState(true); useEffect(() { let isMounted true; const fetchData async () { try { setLoading(true); const response await fetch(url); if (!response.ok) { throw new Error(HTTP ${response.status}); } const result await response.json(); if (isMounted) { setData(result); setError(null); } } catch (err) { if (isMounted) { setError(err.message); } } finally { if (isMounted) { setLoading(false); } } }; fetchData(); return () { isMounted false; }; }, [url]); return { data, error, loading }; }六、团队协作规范6.1 命名规范// ✅ 好的命名 useLocalStorage // 清晰表达用途 useUserData // 返回用户数据 useToggle // 返回切换函数 // ❌ 不好的命名 useStuff // 太模糊 getData // 不以 use 开头 use1 // 无意义6.2 文档注释/** * 获取用户数据的自定义 Hook * param {string} userId - 用户 ID * param {Object} options - 配置选项 * param {boolean} options.autoFetch - 是否自动获取 * returns {Object} { user, loading, error, refetch } */ function useUser(userId, options { autoFetch: true }) { // ... }6.3 Code Review 检查点是否遵守 Hooks 规则依赖数组是否正确是否有内存泄漏风险是否过度使用 useMemo/useCallback是否有无限循环风险七、常见模式总结7.1 状态模式// 单个状态 const [count, setCount] useState(0); // 多个相关状态 const [form, setForm] useState({ name: , email: }); // 复杂状态 const [state, dispatch] useReducer(reducer, initialState);7.2 副作用模式// 挂载时执行 useEffect(() { doSomething(); }, []); // 依赖变化时执行 useEffect(() { doSomething(dep); }, [dep]); // 需要清理 useEffect(() { const subscription subscribe(); return () subscription.unsubscribe(); }, []);7.3 性能优化模式// 缓存计算 const result useMemo(() expensive(a, b), [a, b]); // 缓存函数 const handler useCallback(() doSomething(a), [a]); // 记忆化组件 const MemoChild React.memo(Child);八、快速参考卡Hook用途语法useState状态管理const [state, setState] useState(init)useEffect副作用useEffect(fn, [deps])useContext跨组件传值const value useContext(Context)useReducer复杂状态const [state, dispatch] useReducer(reducer, init)useCallback缓存函数const fn useCallback(fn, [deps])useMemo缓存值const val useMemo(fn, [deps])useRefDOM/可变值const ref useRef(init)useTransition非阻塞更新const [pending, start] useTransition()useDeferredValue延迟值const deferred useDeferredValue(value)useLayoutEffect同步副作用useLayoutEffect(fn, [deps])useImperativeHandle暴露方法useImperativeHandle(ref, fn)useDebugValueDevTools 显示useDebugValue(value)九、小结要点说明设计原则单一职责、可组合、可测试性能优化useMemo、useCallback、React.memo代码质量TypeScript、文档、测试团队规范命名、Review、检查点核心要点Hooks 是现代 React 的核心合理选择和使用 Hooks注重代码质量和性能建立团队最佳实践