React应用中使用Apollo Client作为状态存储的最佳实践探讨
我完全懂这种纠结!当初从Redux迁移到Apollo Cache的时候,也花了好一阵才跳出原来的状态管理思维定式——毕竟Apollo的核心是以数据为中心,和Redux的“状态驱动”思路确实不一样。
结合我自己和身边开发者的实践,分享几个常用的方案,或许能给你一些参考:
直接利用Apollo的查询/突变自动同步
这是最“原生”的Apollo方式:让组件通过useQuery和useMutation直接和缓存交互。比如两个深层组件都查询同一份用户数据,当其中一个组件调用mutation更新数据后,Apollo会自动更新缓存,另一个组件也会随之重渲染拿到最新数据——这本质上就实现了组件间的状态同步,完全不需要额外的props传递或者自定义状态。很多时候我们纠结的“组件通信”,其实Apollo已经通过缓存帮我们解决了。用
useApolloClient直接操作缓存存储UI状态
如果你的场景里有非GraphQL的UI状态(比如侧边栏展开/收起、表单临时数据),也不用单独维护React状态或者Context。可以把这些状态存在Apollo Cache里,通过useApolloClient拿到客户端实例,用writeFragment/writeQuery写入,readFragment/readQuery读取。比如:// 在组件里写入UI状态 const client = useApolloClient(); client.writeFragment({ id: 'uiState', fragment: gql` fragment UIState on Query { sidebarOpen: Boolean } `, data: { sidebarOpen: true }, }); // 在另一个组件里读取 const { data } = useQuery(gql` query GetUIState { sidebarOpen @client } `);这样所有组件都能共享这份UI状态,不用自己搭建状态传递链路。
轻量封装自定义操作(替代Redux式dispatch)
如果确实需要集中处理一些业务逻辑,不用像你现在那样模拟Redux的dispatch结构。可以利用Apollo已经提供的Context(ApolloProvider已经把客户端实例注入到Context了),封装一些自定义的业务函数放在Context里,组件直接调用这些函数即可。比如封装一个updateUserProfile函数,内部调用mutation并处理缓存更新,组件只需要调用这个函数,不用关心底层的Apollo操作。这种方式比自己维护一套dispatch要简洁得多。用Apollo Link处理全局逻辑
如果有全局层面的操作(比如所有mutation的loading状态统一管理、错误拦截),可以写自定义的Apollo Link来处理。比如一个loading状态的Link,每次mutation发起时设置全局loading,完成后清除,所有组件都能通过查询缓存里的全局loading状态来做UI反馈,这也是一种跨组件的状态共享方式。
回到你当前的方案:其实你是把Redux的模式套在了Apollo上,不是说不行,但确实有点冗余——Apollo Cache本身就是一个全局状态容器,完全可以利用它的原生能力来减少自己维护的状态。比如你父组件处理的dispatch操作,如果是数据相关的,直接用useMutation就能搞定,缓存自动同步;如果是UI状态,存在Apollo Cache里就行。
组件通信和状态管理确实是每个应用的核心,很多从Redux转Apollo的开发者都会遇到这个适应期,不用太焦虑~慢慢摸索出适合自己项目的模式就好。
内容的提问来源于stack exchange,提问作者David Thorisson

