ApolloClient:你如何处理缓存中的规范化/嵌套?
Posted
技术标签:
【中文标题】ApolloClient:你如何处理缓存中的规范化/嵌套?【英文标题】:ApolloClient: How do you handle normalization / nesting in the cache? 【发布时间】:2020-07-03 12:57:16 【问题描述】:我不清楚如何使用 ApolloClient InMemoryCache 处理具有嵌套的字段。
例如,我有一个 GraphQL 突变,当将待办事项标记为“已完成”时,它也会将其所有子项标记为“已完成”。在后端,“child”是“parent_id”和“id”之间的关系。
"data":
"upsertTodos": [
"todo":
"__typename": "rc_todos",
"is_completed": true,
"label": "Go to the store",
"id": 1252,
"parent_id": null
"children": [
"__typename": "rc_todos",
"id": 1040,
"is_completed": true,
"label": "Buy some milk",
"parent_id": 1252
],
]
所以,这两个项目都是待办事项(相同的__typename
)并且它们都有id
。我想确保缓存“理解”它们的嵌套和非嵌套状态。
-
ApolloClient 能否“弄清楚”“parent_id”与
“id”或者我可以/应该做些什么来确保它理解这一点以便处理
规范化和反规范化正确吗?
假设“买一些牛奶”已经从
查询
ALL_TODOS
到目前为止已获取所有待办事项的“平面”列表。如果我 writeQuery
反对 ALL_TODOS
只是更新“去商店”待办事项 - 在“儿童”中使用“买一些牛奶”,如上面的响应 - 缓存是否理解“根级别”“买一些牛奶”的todo应该更新吗? (因为它看到__typename
和id
是相同的)或者我是否需要在突变的更新功能中手动进行一些转换才能使其工作?
一般来说,当
使用 InMemoryCache 处理有时被查询的数据
“扁平化”,有时以这种方式“嵌套”?
【问题讨论】:
【参考方案1】:缓存通过仅存储对服务器返回的对象的引用来规范化来自响应的数据。如果一个字段解析为一个对象,该对象将被拉出并单独缓存,该字段的值将只是对该对象的引用(即通过组合__typename
和id
字段创建的缓存键)。如果一个字段解析为一个对象列表,则列表中的每个对象都将被拉出并单独缓存,该字段的值将只是一个对每个对象的引用数组。
由于对象是根据上述缓存键缓存的,因此查询返回特定对象的方式并不重要——如果对象在响应中,它将覆盖已经存在的对象缓存。任何父/子关系都与该机制无关。手动写入缓存也是如此。
当你只是改变一个已经在缓存中的对象时,关键是服务器在其响应中的某个地方返回变异的对象——只要它这样做,缓存就会更新以反映任何变化被制作的对象。
棘手的是影响对象在列表中的成员资格的突变。例如,您可能有一个查询返回已完成的待办事项列表和另一个查询返回待办事项列表。缓存将为每个匹配适当的待办事项对象的查询存储一组缓存键。如果突变将待办事项的状态从待处理更改为已完成,Apollo 无法知道它需要从一个查询中删除它并将其添加到另一个查询(这是只有您的服务器知道的业务逻辑)。在这种情况下,我们必须手动写入缓存并自己更新查询。
同样的原则也适用于创建或删除一个 todo —— Apollo 无法知道“这个突变创建了一个 todo”,即使知道了,它也无法知道它应该添加到哪些列表中。
【讨论】:
你的意思是如果一个突变响应有__typename
和一个以前缓存的 id
,它会知道更新它,但是如果响应有相同的__typename
但有一个 尚未看到 id
,我需要使用 writeQuery 手动将其添加到缓存中吗?此外,对于列表中的成员资格,如果查询明确过滤 is_completed
,您是说它仍然不会“知道”它更新 is_completed
值的事实会影响该查询吗?
具有以前未见过的 id 的对象仍将被缓存——您不需要使用 writeQuery
来缓存它。它只是不会覆盖任何东西,因为它之前没有被缓存过。抱歉,如果不清楚 - 我的意图只是强调对象在响应中的位置(即嵌套的深度)无关紧要,行为是相同的。
是的,这就是我所说的关于列表成员资格的内容。 Apollo 看到带有参数的字段。它不能从字段或参数的名称中暗示突变或查询实际上在服务器端做什么。以上是关于ApolloClient:你如何处理缓存中的规范化/嵌套?的主要内容,如果未能解决你的问题,请参考以下文章