您现在的位置是:首页 > 文章详情

状态管理实战:一次 Redux 到 React Query 的重构之旅

日期:2024-12-12点击:114

"老师,我们的后台管理系统状态管理好混乱啊!"上周二的代码评审会上,小王一脸苦恼地说道。我打开代码仓库看了看,确实问题不小 - 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 = () =&gt; async dispatch =&gt; { 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 =&gt; state.users) useEffect(() =&gt; { dispatch(fetchUsers()) }, [dispatch]) if (loading) return <loading /> if (error) return <error message="{error.message}" /> return ( <div> {users.map(user =&gt; ( <usercard key="{user.id}" user="{user}" /> ))} </div> ) } 

这种方式存在几个明显的问题:

  1. 服务器状态和客户端状态混在一起
  2. 大量重复的样板代码
  3. 缓存和数据同步困难
  4. 性能优化不好做

重构方案

经过团队讨论,我们决定采用"分而治之"的策略:

  1. 使用 React Query 管理 服务器状态
  2. 使用 Zustand 管理本地 UI 状态
  3. 使用 React Hook Form 管理表单状态
// 使用 React Query 管理服务器状态 function useUsers() { return useQuery({ queryKey: ['users'], queryFn: () =&gt; api.get('/users').then(res =&gt; res.data), staleTime: 5 * 60 * 1000, // 5分钟内认为数据是新鲜的 cacheTime: 30 * 60 * 1000 // 缓存30分钟 }) } // 使用 Zustand 管理 UI 状态 interface UIStore { theme: string sidebar: boolean setTheme: (theme: string) =&gt; void toggleSidebar: () =&gt; void } const useUIStore = create<uistore>(set =&gt; ({ theme: 'light', sidebar: true, setTheme: theme =&gt; set({ theme }), toggleSidebar: () =&gt; set(state =&gt; ({ sidebar: !state.sidebar })) })) // 使用 React Hook Form 管理表单 function UserForm() { const { register, handleSubmit, formState: { errors } } = useForm<userformdata>() const queryClient = useQueryClient() const mutation = useMutation({ mutationFn: (data: UserFormData) =&gt; api.post('/users', data), onSuccess: () =&gt; { // 成功后使缓存失效 queryClient.invalidateQueries({ queryKey: ['users'] }) } }) const onSubmit = handleSubmit(data =&gt; { mutation.mutate(data) }) return ( <form onSubmit="{onSubmit}"> <input {...register('name', { required: true })}> {errors.name &amp;&amp; <span>名字是必填的</span>} <button type="submit">提交</button> </form> ) } 

重构过程

为了平滑过渡,我们采用了渐进式重构策略:

  1. 首先创建一个自定义 Hook 封装数据获取逻辑:
// hooks/useResource.ts function useResource<t>(resource: string) { const query = useQuery({ queryKey: [resource], queryFn: () =&gt; api.get(`/${resource}`).then(res =&gt; res.data), // 配置缓存策略 staleTime: 5 * 60 * 1000, cacheTime: 30 * 60 * 1000, // 乐观更新配置 optimisticResults: true, // 重试策略 retry: 3, retryDelay: attemptIndex =&gt; Math.min(1000 * 2 ** attemptIndex, 30000) }) const mutation = useMutation({ mutationFn: (data: Partial<t>) =&gt; api.post(`/${resource}`, data), onSuccess: () =&gt; { // 更新缓存 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 =&gt; ( <usercard key="{user.id}" user="{user}" /> ))} </div> ) } 
  1. 然后逐步迁移状态管理:
// 新的状态管理结构 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: () =&gt; api.get('/roles').then(res =&gt; res.data), // 只有当有用户数据时才获取角色 enabled: !!users }) // 使用 memo 优化渲染 const userCards = useMemo(() =&gt; users?.map(user =&gt; <usercard key="{user.id}" user="{user}" role="{roles?.find(role" => role.id === user.roleId)} /&gt;), [users, roles]) if (isLoading) return <loading /> return <div>{userCards}</div> } 

效果验证

重构后,我们观察到了明显的改善:

  1. 代码更清晰,职责划分明确
  2. 缓存管理更智能,性能提升明显
  3. 开发效率提高,不用写那么多样板 代 码
  4. 数据同步问题大大减少

最让我印象深刻的是小王的反馈:"现在代码写起来舒服多了,不用担心状态同步的问题!"

经验总结

这次重构让我们学到了很多:

  1. 不同类型的状态要用不同的工具管理
  2. 缓存策略要根据业务场景来设计
  3. 渐进式重构比大规模重写更可控
  4. 好的抽象能大大提高开发效率

就像整理房间一样,不同类型的物品要放在不同的地方。把衣服、书籍、电子产品分类存放,不仅容易找,也更好维护。状态管理也是一样,合适的工具管理合适的状态,才能让代码更清晰、更好维护。

写在最后

状态管理没有银弹,关键是要根据实际需求选择合适的方案。就像选择家具一样,不是越贵越好,而是要适合自己的需求。

有什么问题欢迎在评论区讨论,让我们一起探讨状态管理的最佳实践!

> 如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~ </usercard></user></user></t></t></userformdata></uistore>

原文链接:https://my.oschina.net/u/8534996/blog/16683429
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章