如何在多个视图中处理突变后的缓存更新?

Posted

技术标签:

【中文标题】如何在多个视图中处理突变后的缓存更新?【英文标题】:How to handle cache update after mutation in multiple views? 【发布时间】:2020-09-19 02:36:22 【问题描述】:

这更像是一个悬而未决的问题,但希望它不会被删除。

我正在使用 react 和 apollo,尽管问题更笼统。

假设我的应用中有 3 个不同的视图,它们都使用相似(但不相同)的数据。 它们都使用单独的查询,但每个查询都使用通用操作,但返回的数据略有不同。

假设我在某处有一个突变,它向数据添加了一些东西(想想一个项目列表和一个正在添加的新项目)。

假设在突变后我想更新缓存以反映该变化。我正在使用 read/writeQuery 进行更新。 使用此设置,我需要更新 3 个查询 - 这将成为维护的噩梦。

经过一番阅读,我发现我做错了——我现在创建了一个查询——现在我只需要在突变后更新那个单一的查询,我的所有视图都会自动更新。 然而问题是这个查询现在必须下载所有 3 个视图组合所需的所有数据 - 感觉这非常低效,因为一些视图将获取他们永远不会使用的数据。

有没有更好的方法?

请注意,read/writeFragment 将不起作用,因为它们不会更新基础查询 - 请查看此答案,例如:https://***.com/a/50349323/2874705

如果您需要更具体的示例,请在评论中告诉我。

总而言之,我认为在这个设置中我会更好地处理全局状态并避免一起使用 apollo 缓存 - 但是我觉得被骗了,因为 apollo 承诺会解决状态问题 :)

编辑

这是一个具体的例子:

假设我们的 graphql 模式是这样定义的:

type Post 
    id: ID!
    title: String!
    body: String
    published: Boolean!



type Query 
    posts(published: Boolean): [Post!]!



type Mutation 
  createDraft(body: String!, title: String!): Post
  publish(id: Int!): Post

现在,我们在客户端创建 3 个查询和 2 个突变

query PostTitles 
    posts 
        id
        title
    


query Posts 
    posts 
        id
        title
        body
        published
    


query PublishedPosts 
    posts (published: true) 
        id
        title
        body
        published
    


mutation CreateDraftPost ($body: String!, $title: String!) 
    createDraft(body: $body, title: $title) 
        id
        title
        body
        published
    


mutation PublishPost ($id:ID!) 
    publish (id: $id) 
        id
        published
    

请注意 createDraft 使用默认 false published 值创建帖子。

如何在不使用 refetchQueries 或手动更新每个查询的情况下使用这些突变中的任何一个来创建或发布帖子并更新所有 3 个缓存查询? 我认为真正的问题是这些查询中的每一个都单独存储在 apollo 内存缓存中。

【问题讨论】:

是的,一个最小的可重现示例会有很大帮助。 @DanieleRicci 我添加了一个示例 - 如果您需要更多信息,请告诉我 【参考方案1】:

听起来您已经研究并使用了更新参数,该参数可以传递给从 useMutation 返回的变异函数。您可能正在使用 proxy.readQuery 和 proxy.writeQuery 来更新它(或者让这种魔法在后台发生)。如果没有,这里是documentation。

另一种概念相似但细节更精细的方法是使用 proxy.readFragment 和 proxy.writeFragment。您可以将类型上的一组属性指定为片段的一部分,并在新数据进入时更新该片段。好的部分是该片段可以在任意数量的查询中使用,如果您更新片段,这些查询将更新。

fragment documentations

【讨论】:

感谢您的回答。是的,我使用了 read/writeQuery,我应该在问题中说清楚。但是,由于我在问题中解释的原因,这是有问题的 - 您必须更新多个查询或创建单个查询,这两种解决方案似乎都很糟糕。 writeFragment 的问题在于它是不可观察的——它不会自动更新查询。【参考方案2】:

根据我的经验,应该是这样的。


CreateDraftPost突变情况下:

    您调用突变并传递update 函数。在此update 函数中,您通过创建Post 的新片段来修改根查询posts 的缓存,然后将此片段添加到posts 中。看到这个:https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates 由于PostTitlesPosts 都依赖于根查询posts(只是查询的字段不同)并且您刚刚添加到posts 中的Post 的新片段具有足够的字段,您的 PostTitlesPosts 应该会自动反映更改。 由于CreateDraftPost 始终使用published 创建草稿,默认为false。您无需更新与 PublishedPosts 查询相关的任何内容。

PublishPost突变情况下:

    您调用突变,返回的结果是带有更新字段的Postidpublished)。通过 Apollo GraphQL 缓存机制,这个Post(由id 标识)将在它涉及的任何查询中更新。看到这个:https://www.apollographql.com/docs/react/data/mutations/#updating-a-single-existing-entity 但是,您需要手动更新PublishedPost 查询。通过在突变调用中提供update 函数来做到这一点。在这个update 函数中,您将首先readQueryPublishedPost,从返回的数据中创建一个新的Post,最后writeQuery 将此帖子添加到PublishedPost 结果中。参考这个:https://www.apollographql.com/docs/react/caching/cache-interaction/#combining-reads-and-writes

refetchQueries怎么样:

CreateDraftPost 突变的情况下,仅重新获取Posts 查询就足够了(PostTitles 应相应更新),因为PostsPostTitles 都依赖于相同的根查询postsPosts 中的字段也覆盖了 PostTitles 中的字段 在 PublishPost 突变的情况下,我宁愿重新获取 PublishedPost 查询以避免做整个 update 事情(因为我很懒,我认为重新获取 1 个查询不会花费我太多)

【讨论】:

非常感谢您的回答。总而言之,感觉就像我可以只使用一个查询并将所有道具放在那里(这就是我现在正在做的事情)或者(我正在考虑)完全摆脱阿波罗并使用全球商店 我花了一段时间才了解 Apollo GraphQL 以及它的所有缓存机制是如何工作的。但是,一旦你理解了,感觉就会好很多。例如,在手动维护全局 redux 存储的情况下,您必须处理加载/错误状态、规范化响应 (github.com/paularmstrong/normalizr)、缓存响应、使缓存无效、在组件卸载时取消获取。我认为 Apollo GraphQL 已经很好地涵盖了这些情况。 好吧,你当然不会单独使用 redux,因为你可以拥有另一个库(现在已经添加到 redux 中)。老实说,我对阿波罗的体验并不好。它有令人困惑的 API 并且是错误的恕我直言 - 例如间隔处理不当,我通常会使用 useInterval 挂钩;一些方法返回承诺一些不;代码生成对语法错误保持沉默;更不用说文档有多糟糕了;我可以继续在与阿波罗工作中失去的无数小时,老实说我不会向任何人推荐它 我明白了,这也取决于用例。我希望你做得很好。

以上是关于如何在多个视图中处理突变后的缓存更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何在突变后更新阿波罗缓存(使用过滤器查询)

变异后的Apollo缓存未更新

如何从突变查询中更新 Apollo 数据存储/缓存,更新选项似乎没有触发

应该如何对列表中的项目进行重新排序,并更新多个对象,如何使用 GraphQL 突变进行持久化?

如何在突变之前更新 apollo 缓存状态,以进行 Like、Upvote 等操作? [关闭]

如何在突变时使用 urql 更新 graphql 缓存,其中初始查询响应不包括所需的 __typename?