跨组件更新Apollo Client缓存后如何正确读取缓存数据
核心结论
你遇到的是Apollo Client的预期行为,不是程序故障。
原因说明
useApolloClient()返回的是静态的Apollo Client实例引用,本身不会因为缓存内容变化触发组件重渲染。你写的依赖client的useEffect只会在组件首次挂载、client实例变更时执行一次——这时候<Navigation />组件发起的用户信息查询还处于请求阶段,InMemoryCache里还没有对应数据,读出来自然是null。
后续查询完成、缓存更新时,<MainContent />组件没有订阅该缓存条目的变化,不会触发重渲染,对应的effect也不会重新执行,自然拿不到最新值。加固定延迟能读到值只是刚好匹配了接口返回的耗时,网络波动时随时会失效,完全不可靠。
官方推荐解决方案
不需要引入Redux同步状态,Apollo Client原生提供了响应式读缓存的能力,以下方案都不会发起冗余的GraphQL请求:
- 优先使用
useQuery配置cache-only拉取策略(最简便,官方首推)
直接在第二个组件内调用useQuery读取同一份查询文档,指定拉取策略为仅读缓存,Apollo会自动为你订阅缓存更新,缓存写入后组件会自动重渲染拿到最新值,不会重复发网络请求:// MainContent组件内 const { data } = useQuery(MeDocument, { fetchPolicy: 'cache-only' // 强制仅读取本地缓存,不会发起网络请求 }); const isLogin = !!data?.me; // 缓存更新后这里会自动拿到最新值 - 自定义场景下手动订阅缓存更新
如果你有特殊逻辑需要手动操作缓存实例,可以通过watchQuery订阅对应查询的更新,注意组件卸载时要取消订阅避免内存泄漏:const client = useApolloClient(); const [currentUser, setCurrentUser] = useState(() => { // 初始化时先读一次缓存 return client.cache.readQuery({ query: MeDocument }); }); useEffect(() => { // 订阅对应查询的缓存变更 const watcher = client.watchQuery({ query: MeDocument, fetchPolicy: 'cache-only' }); const subscription = watcher.subscribe(res => { setCurrentUser(res.data); }); return () => subscription.unsubscribe(); }, [client]);
注意:不要使用固定延迟读取缓存的写法,接口耗时受网络环境、服务端性能影响波动很大,无论延迟设置为多少都存在读值失败的可能,属于典型的反模式。
你之前考虑的将查询结果同步到Redux的方案完全没有必要,Apollo的InMemoryCache本身就具备跨组件状态广播能力,额外同步到Redux只会增加维护成本,还容易出现双端状态不一致的问题。
内容的提问来源于stack exchange,提问作者mcclosa
相关产品推荐
相关产品推荐

