状态管理实战:一次 Redux 到 React Query 的重构之旅
"老师,我们的后台管理系统状态管理好混乱啊!"上周二的代码评审会上,小王一脸苦恼地说道。我打开代码仓库看了看,确实问题不小 - Redux store 里堆满了各种数据,有本地状态,有服务器数据,还有一些缓存,导致代码难以维护,性能也受到影响。
说实话,这个问题在中后台项目中很常见。随着项目的发展,状态管理往往会变得越来越复杂。今天就来分享一下我们是如何通过重构解决这个问题的。
问题的症状
首先,让我们看看重构前的代码是什么样子:
// 原来的 Redux store interface AppState { // 本地 UI 状态 ui: { theme: string sidebar: boolean modal: { visible: boolean type: string } } // 服务器数据 users: { list: User[] loading: boolean error: Error | null lastUpdated: number } products: { list: Product[] loading: boolean error: Error | null lastUpdated: number } // 表单状态 forms: { userForm: { values: any errors: any touched: boolean[] } productForm: { values: any errors: any touched: boolean[] } } } // 获取用户数据的 action const fetchUsers = () => async dispatch => { dispatch({ type: 'FETCH_USERS_START' }) try { const response = await api.get('/users') dispatch({ type: 'FETCH_USERS_SUCCESS', payload: response.data, lastUpdated: Date.now() }) } catch (error) { dispatch({ type: 'FETCH_USERS_ERROR', error }) } } // 组件中的使用 function UserList() { const dispatch = useDispatch() const { list: users, loading, error } = useSelector(state => state.users) useEffect(() => { dispatch(fetchUsers()) }, [dispatch]) if (loading) return <loading /> if (error) return <error message="{error.message}" /> return ( <div> {users.map(user => ( <usercard key="{user.id}" user="{user}" /> ))} </div> ) }
这种方式存在几个明显的问题:
- 服务器状态和客户端状态混在一起
- 大量重复的样板代码
- 缓存和数据同步困难
- 性能优化不好做
重构方案
经过团队讨论,我们决定采用"分而治之"的策略:
- 使用 React Query 管理 服务器状态
- 使用 Zustand 管理本地 UI 状态
- 使用 React Hook Form 管理表单状态
// 使用 React Query 管理服务器状态 function useUsers() { return useQuery({ queryKey: ['users'], queryFn: () => api.get('/users').then(res => res.data), staleTime: 5 * 60 * 1000, // 5分钟内认为数据是新鲜的 cacheTime: 30 * 60 * 1000 // 缓存30分钟 }) } // 使用 Zustand 管理 UI 状态 interface UIStore { theme: string sidebar: boolean setTheme: (theme: string) => void toggleSidebar: () => void } const useUIStore = create<uistore>(set => ({ theme: 'light', sidebar: true, setTheme: theme => set({ theme }), toggleSidebar: () => set(state => ({ sidebar: !state.sidebar })) })) // 使用 React Hook Form 管理表单 function UserForm() { const { register, handleSubmit, formState: { errors } } = useForm<userformdata>() const queryClient = useQueryClient() const mutation = useMutation({ mutationFn: (data: UserFormData) => api.post('/users', data), onSuccess: () => { // 成功后使缓存失效 queryClient.invalidateQueries({ queryKey: ['users'] }) } }) const onSubmit = handleSubmit(data => { mutation.mutate(data) }) return ( <form onSubmit="{onSubmit}"> <input {...register('name', { required: true })}> {errors.name && <span>名字是必填的</span>} <button type="submit">提交</button> </form> ) }
重构过程
为了平滑过渡,我们采用了渐进式重构策略:
- 首先创建一个自定义 Hook 封装数据获取逻辑:
// hooks/useResource.ts function useResource<t>(resource: string) { const query = useQuery({ queryKey: [resource], queryFn: () => api.get(`/${resource}`).then(res => res.data), // 配置缓存策略 staleTime: 5 * 60 * 1000, cacheTime: 30 * 60 * 1000, // 乐观更新配置 optimisticResults: true, // 重试策略 retry: 3, retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000) }) const mutation = useMutation({ mutationFn: (data: Partial<t>) => api.post(`/${resource}`, data), onSuccess: () => { // 更新缓存 query.invalidate() } }) return { data: query.data, isLoading: query.isLoading, error: query.error, create: mutation.mutate, isCreating: mutation.isLoading } } // 使用示例 function UserList() { const { data: users, isLoading, error } = useResource<user>('users') if (isLoading) return <loading /> if (error) return <error message="{error.message}" /> return ( <div> {users.map(user => ( <usercard key="{user.id}" user="{user}" /> ))} </div> ) }
- 然后逐步迁移状态管理:
// 新的状态管理结构 interface AppState { // Zustand 管理 UI 状态 ui: UIStore // React Query 管理服务器状态 // - 用户数据 // - 产品数据 // - 订单数据 // React Hook Form 管理表单状态 // - 用户表单 // - 产品表单 } // 性能优化 function UserList() { const { data: users, isLoading } = useResource<user>('users') // 使用 React Query 的内置缓存 const { data: roles } = useQuery({ queryKey: ['roles'], queryFn: () => api.get('/roles').then(res => res.data), // 只有当有用户数据时才获取角色 enabled: !!users }) // 使用 memo 优化渲染 const userCards = useMemo(() => users?.map(user => <usercard key="{user.id}" user="{user}" role="{roles?.find(role" => role.id === user.roleId)} />), [users, roles]) if (isLoading) return <loading /> return <div>{userCards}</div> }
效果验证
重构后,我们观察到了明显的改善:
- 代码更清晰,职责划分明确
- 缓存管理更智能,性能提升明显
- 开发效率提高,不用写那么多样板 代 码
- 数据同步问题大大减少
最让我印象深刻的是小王的反馈:"现在代码写起来舒服多了,不用担心状态同步的问题!"
经验总结
这次重构让我们学到了很多:
- 不同类型的状态要用不同的工具管理
- 缓存策略要根据业务场景来设计
- 渐进式重构比大规模重写更可控
- 好的抽象能大大提高开发效率
就像整理房间一样,不同类型的物品要放在不同的地方。把衣服、书籍、电子产品分类存放,不仅容易找,也更好维护。状态管理也是一样,合适的工具管理合适的状态,才能让代码更清晰、更好维护。
写在最后
状态管理没有银弹,关键是要根据实际需求选择合适的方案。就像选择家具一样,不是越贵越好,而是要适合自己的需求。
有什么问题欢迎在评论区讨论,让我们一起探讨状态管理的最佳实践!
> 如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~ </usercard></user></user></t></t></userformdata></uistore>

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
One API 替代品 Chat Nio 安装与使用教程
有这样一位初中生 ,他在初一下学期发起了一个项目,专门用来给他的朋友们免费体验 GPT 模型。 到了八年级的暑假,他决定把这个项目开源出来,并且正式命名为 Chat Nio,同时项目的定位为一站式 LLM 模型管理平台。 到了九年级 (今年年初),Chat Nio 项目月收入已达到 ¥5w。 中考结束后,项目就被收购了,作者赚到了人生的第一个 100w,同时也变成了一名 CTO... 看看人家初中在干嘛,再看看自己... 不说了,我们还是来看看 Chat Nio 项目吧。 一句话总结:Chat Nio 就是 ChatGPT Next Web 和 One API 的究极进化版。 为什么需要 Chat Nio? 当前 AIGC 商业平台主要分为两类: 以 ChatGPT Next Web 为代表的轻量级部署项目: ✅ 精美的用户界面 ✅ 便捷的个人部署 ❌ 对话同步依赖复杂的 WebDav 配置 ❌ 计费模式单一 ❌ 文件处理不够便捷 ❌ 缺乏完整的 API 分发能力 以 One API 为代表的 API 分发项目: ✅ 强大的渠道管理 ✅ 完整的 API 分发 ❌ 缺乏直观的用户界面 ❌...
- 下一篇
【直播预告】开发 AI 应用不难,FastGPT 作者手把手教
开源中国 OSCHINA的直播栏目《开源项目老牌与新秀》第1期,邀请了 FastGPT 的作者余金隆,手把手教大家怎么用 FastGPT 构建 AI 应用。 FastGPT 是一个基于 LLM 大模型的开源 AI 知识库构建平台,提供了开箱即用的数据处理、模型调用、RAG 检索、可视化 AI 工作流编排等能力,帮助用户轻松构建复杂的 AI 应用。自去年 3 月份开源以来,FastGPT 在 GitHub 的 Star 数直接冲到了 18.9K,相当火爆。 直播主题:构建复杂 AI 应用,有开源项目 FastGPT 就够了 直播时间:12月13日周五 19:00-20:00 直播平台:视频号“OSC 开源社区” 直播嘉宾:FastGPT 作者余金隆 直播亮点: Star 高达 18.9k, FastGPT 是如何养成的? 数据处理、模型调用、RAG 检索、可视化工作流编排,FastGPT 有多强? 只需 15分钟,手把手教你写 AI 日报机器人 定制 AI 客服,长字幕翻译,Dalle3 绘图,用 FastGPT 能做什么? 揭秘 FastGPT 的未来迭代计划,展望技术发展新方向 Q&...
相关文章
文章评论
共有0条评论来说两句吧...