Apollo Client v3中能否为不同__typename配置OptimisticResponse?
结论
完全可以实现,不需要调整现有Query和Mutation的基础结构。
为什么自动更新失效
Apollo Client v3的缓存是归一化设计,所有带__typename和唯一主键的对象会单独存成缓存条目,主键默认是__typename:id的格式。你执行更新关联关系的Mutation后,返回的__typename: 'Relation'数据确实已经写入缓存了,但所有引用了Relation条目的父级嵌套数组(比如Entity下的relation字段、你提到的其他__typename对象下的关联列表)不会自动更新引用,UI读取到的还是旧的数组引用,自然不会触发重渲染。
实现方案
方案1:通过Mutation的update回调手动更新缓存(最稳定可控,优先选)
这是解决嵌套缓存更新最通用的方案,不需要依赖乐观响应也能生效,步骤如下:
- 在执行Mutation的配置项中传入
update函数,函数接收两个参数:当前缓存实例、Mutation返回的真实数据 - 在函数内先读取所有页面上用到关联Relation列表的缓存位置的数据
- 对拿到的旧数组做增/删/改操作,把Mutation返回的新Relation数据替换到对应位置,再写回缓存
- 所有引用了对应缓存位置的UI组件会自动同步更新
基础代码示例:
const [updateRelation] = useMutation(UPDATE_RELATION_MUTATION, { update(cache, { data: { updatedRelation } }) { // 读取页面绑定的Query对应的缓存数据,variables必须和页面调用Query时的入参完全一致 const existingEntityData = cache.readQuery({ query: GET_ENTITY_QUERY, variables: { entityId: currentEntityId } }) if (!existingEntityData) return // 更新嵌套的relation数组:替换同id旧项,新增就push,删除就filter对应项 const newRelationList = existingEntityData.entity.relation.map(item => { return item.id === updatedRelation.id ? updatedRelation : item }) // 将更新后的数据写回缓存 cache.writeQuery({ query: GET_ENTITY_QUERY, variables: { entityId: currentEntityId }, data: { entity: { ...existingEntityData.entity, relation: newRelationList } } }) // 如果其他__typename对象下也嵌套了同一份Relation列表,按相同逻辑读缓存、更新、写回即可 } })
如果关联列表挂载在其他Query下,只要在update里按相同逻辑处理对应Query的缓存即可,和嵌套层级没有关系。
方案2:配合Optimistic Response实现无延迟UI更新
如果需要点击操作后立刻反馈、不用等接口返回再刷新,可以在上面update方案的基础上,给Mutation加optimisticResponse配置,提前传入预期的返回结构:
const [updateRelation] = useMutation(UPDATE_RELATION_MUTATION, { // 乐观响应,会先于服务端返回触发update逻辑 optimisticResponse: { __typename: 'Mutation', updateRelation: { __typename: 'Relation', id: `temp-${Date.now()}`, // 临时id,服务端返回真实数据后会自动替换 // 填入列表渲染需要用到的所有字段的预期值 name: '更新后的关联名称', // ...其余业务字段 } }, // 保留方案1中的update函数即可,会先执行乐观数据更新,服务端返回后再用真实数据覆盖更新 update(cache, { data: { updateRelation } }) { // 和之前的更新逻辑完全一致 } })
这个方案可以实现操作后UI立刻更新,不会有接口等待的空白感,交互体验更好。
注意事项
- 不管是Mutation真实返回的数据,还是乐观响应里填的模拟数据,必须带正确的
__typename字段,以及列表渲染用到的所有业务字段,否则会出现缓存字段缺失导致的渲染异常 cache.readQuery和cache.writeQuery传入的variables必须和页面上调用对应Query时传入的variables完全一致,否则会读不到缓存、或者更新到错误的缓存位置- 如果关联关系更新逻辑在多个页面复用,不想每次重复写update逻辑,可以在Apollo Client初始化的
typePolicies里,给对应父类型的relation字段配置merge函数,统一处理数组合并逻辑,适合结构固定的场景。
内容的提问来源于stack exchange,提问作者Bruce Lee
相关产品推荐
相关产品推荐

