Apollo GraphQL:类型策略中合并函数的用例?

Bar*_*art 6 javascript caching apollo react-apollo apollo-client

我之前在Apollo\xe2\x80\x99s 社区聊天中问过这个问题中问过这个问题,但我将其重新发布到这里,因为两周后 \xe2\x80\x99 没有得到答复。

\n

这个问题是关于Apollo 3.0引入的新缓存的。此更新弃用了本地查询,转而使用 \xe2\x80\x9ctype 策略\xe2\x80\x9d(或 \xe2\x80\x9cfield 策略\xe2\x80\x9d),其中包括多个用于处理缓存的工具,例如可以为每个 GraphQL 类型定义一个merge函数,以指定传入数据应如何与缓存中的现有数据合并。

\n

我\xe2\x80\x99m 想知道哪些用例更适合该函数,而不是通过或merge编写预先计算的更新值。writeQuerywriteFragment

\n

让\xe2\x80\x99s 说我有一个包含对象的数组,并且该数组是另一种类型的属性。例如:

\n
type Person {\n  hobbies: [Hobby!]!\n}\n
Run Code Online (Sandbox Code Playgroud)\n

该应用程序允许我删除、添加和更新该数组中的元素。应该hobbies使用该merge功能吗?以前,我在本地突变解析器中编写了此逻辑,但这些已被弃用。我可以通过为每个解析器定义一个函数来轻松模仿旧行为,该函数包含添加/删除/更新逻辑,并用于writeFragment存储结果。

\n

我可以看到使用的好处,merge因为(我认为)it\xe2\x80\x99是较低的抽象层,但是它可以在传入数组的情况下使用吗hobbies?据我所知,它的工作方式意味着我们\xe2\x80\x99d 必须通过传入的输入来推断突变的类型。例如:

\n
    \n
  • 如果传入的数组少包含一项(或仅包含已删除的项),则我们执行删除操作,并且我们可以假设所有其他元素保持不变。
  • \n
  • 如果传入的数组只包含一个元素并且它是新的,我们执行添加操作,并将这一元素合并到数组中
  • \n
  • 如果传入数组包含相同数量的元素(或者只有一个更新的项目),我们必须更新其中一个,并且现在必须找出要替换的元素(让\xe2\x80\x99s 假设,由于实现的原因,它无法直接更新单个Hobby,因为其他爱好也可能受到影响,例如由于不重叠的时间限制)。
  • \n
\n

这似乎不如镜像旧方法那么优雅,在旧方法中,在hobbies调用之前计算新值writeFragment新值。

\n

使用有性能优势吗mergehobbies使用 时,现有的和不受影响的项目是否保留在原处merge,并在使用 时被覆盖writeFragment?假设新计算的数据传递到writeFragment包含一个数组,其中包含未修改元素的浅副本。

\n

非常感谢您为我解决了这个问题!

\n

Bar*_*art 0

与此同时,我实现了一个类似于Apollo 文档中概述的 \xe2\x80\x99s 的合并函数。此函数尽可能多地重用数组中的项目existing,并从其他位置获取项目incoming。相等性是根据 object 检查的id,它几乎总是存在于我的应用程序中。

\n

这种方法的唯一(次要)缺点似乎是它不能处理数组中的项目被更改的情况,但这种操作是在不返回的单独突变中完成的无论如何,一个数组。

\n

为了后人的利益,我的实现如下:

\n
import type { FieldPolicy, Reference } from \'@apollo/client\'\n\n/**\n * Based on example in Apollo docs\n *\n * @see https://www.apollographql.com/docs/react/caching/cache-field-behavior/#merging-arrays-of-non-normalized-objects\n */\nexport const mergeArrayByIdFieldPolicy: FieldPolicy<Reference[]> = {\n  // eslint-disable-next-line @typescript-eslint/default-param-last\n  merge: (existing = [], incoming = [], { readField, mergeObjects }) => {\n    const merged = [...incoming]\n    const existingIds = existing.map((item) => readField<UUID>(\'id\', item))\n\n    merged.forEach((item, index) => {\n      const itemId = readField<UUID>(\'id\', item)\n      const existingIndex = existingIds.findIndex((id) => id === itemId)\n      if (existingIndex !== -1) {\n        merged[index] = mergeObjects(existing[existingIndex], merged[index])\n      }\n    })\n    return merged\n  },\n}\n
Run Code Online (Sandbox Code Playgroud)\n