首页
/ Apollo Client 中使用 cache.modify 处理订阅数据的类型安全实践

Apollo Client 中使用 cache.modify 处理订阅数据的类型安全实践

2025-05-11 05:48:13作者:明树来

背景介绍

在现代前端开发中,GraphQL 订阅(Subscription)是实现实时功能的重要手段。Apollo Client 作为流行的 GraphQL 客户端,提供了强大的缓存机制来处理订阅数据。然而,在使用 TypeScript 开发时,如何类型安全地操作缓存成为了一个常见挑战。

核心问题

当应用程序通过订阅接收到新数据时,需要将这些数据合并到现有的缓存中。典型的场景包括:

  1. 帖子(Post)和评论(Comment)的关系
  2. 对话(Conversation)和步骤(Step)的关系

在这些关系中,父实体(如 Post 或 Conversation)通常通过连接(Connection)字段(如 comments 或 steps)关联子实体列表。当订阅推送新数据时,我们需要将这些新数据追加到缓存中的对应列表。

解决方案演进

初始方案:直接修改缓存

最初的尝试是直接使用 cache.modify 方法修改缓存:

cache.modify({
  id: cache.identify({
    __typename: "Post",
    id: comment.post.id,
  }),
  fields: {
    comments(existing) {
      return {
        ...existing,
        edges: [...existing.edges, { node: comment }],
      };
    },
  },
});

这种方法虽然能工作,但存在明显的类型安全问题:

  • existing 参数类型为 any,缺乏类型检查
  • 无法处理缓存引用(Reference)的情况
  • 忽略了连接字段的完整类型定义(如缺少 cursor 等必需字段)

改进方案:使用 readField 辅助方法

为了解决类型安全问题,引入了 readField 方法:

cache.modify<Conversation>({
  id: cache.identify({
    __typename: "Conversation",
    id: step.conversation.id,
  }),
  fields: {
    steps(existing, { readField }) {
      const edges = readField("edges", existing) || [];
      return {
        ...existing,
        edges: [...edges, { node: step }],
      };
    },
  },
});

这种方法仍然存在问题:

  • readField 返回类型过于宽泛
  • 无法确保返回的数据符合 Connection 类型的完整定义
  • 类型断言可能导致运行时错误

最终方案:结合 updateQuery 和类型保护

经过多次尝试,最终确定了一个更可靠的方案:

const onNewStep = (step: DetailedStepFragment) => {
  cache.updateQuery(
    {
      query: GQL.Conversation.StepsQuery,
      variables: { id: step.conversation.id, last: 8 },
    },
    (existing) => {
      if (existing?.node?.__typename !== "Conversation") return;
      const node = frag(GQL.Conversation.WithSteps, existing.node);
      return {
        ...existing,
        node: {
          ...node,
          steps: {
            ...node.steps,
            edges: [
              ...node.steps.edges,
              { __typename: "ConversationStepEdge", node: step },
            ],
          },
        },
      };
    }
  );
};

这个方案的优点:

  1. 通过 updateQuery 直接操作查询结果,类型更明确
  2. 使用类型保护确保操作的安全性
  3. 完整维护了连接类型的所有必需字段
  4. 与现有查询结构保持一致

最佳实践建议

  1. 优先使用 updateQuery:当需要精确控制缓存更新时,updateQuery 通常比 modify 更类型安全。

  2. 正确处理连接类型:确保在添加新边(edge)时包含所有必需字段,如 __typenamecursor

  3. 考虑分页策略:如果使用了 relayStylePagination,需要注意手动更新可能会绕过分页策略。

  4. 使用最新 API:Apollo Client 3.11+ 提供了改进的 useSubscription hook,支持 ignoreResults 选项,可以更好地控制组件重渲染。

  5. 避免 useEffect 中的订阅处理:推荐使用 onData 回调来处理订阅数据,而不是依赖 useEffect

总结

在 Apollo Client 中处理订阅数据时,类型安全是关键考量。通过合理选择缓存操作方法(modifyupdateQuery)并结合 TypeScript 的类型保护,可以构建既安全又高效的实时数据更新机制。开发者应当根据具体场景选择最适合的方法,同时遵循 Apollo Client 的最佳实践。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
22
6
docsdocs
OpenHarmony documentation | OpenHarmony开发者文档
Dockerfile
205
2.18 K
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
208
285
pytorchpytorch
Ascend Extension for PyTorch
Python
62
95
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
977
575
nop-entropynop-entropy
Nop Platform 2.0是基于可逆计算理论实现的采用面向语言编程范式的新一代低代码开发平台,包含基于全新原理从零开始研发的GraphQL引擎、ORM引擎、工作流引擎、报表引擎、规则引擎、批处理引引擎等完整设计。nop-entropy是它的后端部分,采用java语言实现,可选择集成Spring框架或者Quarkus框架。中小企业可以免费商用
Java
9
1
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
550
86
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
1.02 K
399
communitycommunity
本项目是CANN开源社区的核心管理仓库,包含社区的治理章程、治理组织、通用操作指引及流程规范等基础信息
393
27
MateChatMateChat
前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com
1.2 K
133