Apollo GraphQL:类型策略中合并函数的用例?
Apollo GraphQL: use case for merge function in type policy?
我之前曾在 Apollo’s community chat 上问过这个问题,但由于两周后仍未得到答复,所以我将其重新发布在这里。
这个问题是关于Apollo 3.0引入的新缓存的。此更新弃用了本地查询,取而代之的是“类型策略”(或“字段策略”),其中包括几个用于使用缓存的工具,例如可以为每个 GraphQL 类型定义的 merge
函数来指定如何传入数据应与缓存中的现有数据合并。
我想知道哪些用例更适合 merge
函数,而不是通过 writeQuery
或 writeFragment
.
写入预先计算的更新值
假设我有一个包含对象的数组,而这个数组是另一种类型的 属性。例如:
type Person {
hobbies: [Hobby!]!
}
该应用程序允许我删除、添加和更新此数组中的元素。 hobbies
应该使用 merge
函数吗?以前,我在本地突变解析器中编写了这个逻辑,但这些已被弃用。通过为这些解析器中的每一个定义一个函数,我可以很容易地模仿旧的行为,它包含 add/remove/update 逻辑,并使用 writeFragment
来存储结果。
我可以看到使用 merge
的好处,因为(我认为)它是一个较低的抽象层,但是它可以在传入 hobbies
数组的情况下使用吗?据我所知,它的工作方式意味着我们必须通过传入的输入来推断突变的类型。例如:
- 如果传入的数组少了一个元素(或者只包含了被删除的元素),我们做了一个删除操作,我们可以假设所有其他元素都保持不变。
- 如果传入的数组只包含一个元素,并且是新的,我们做了一个添加操作,并将这个元素合并到数组中
- 如果传入数组包含相同数量的元素(或只有一个更新的项目),我们必须更新一个并且现在必须找出要替换的一个(让我们假设,由于实现,它是无法直接更新单个
Hobby
,因为其他爱好也可能受到影响,例如由于非重叠时间限制)。
这似乎不如在调用 writeFragment
.
之前计算 hobbies
的新值的旧方法优雅
使用 merge
有性能优势吗? hobbies
中的现有和不受影响的项目是否在使用 merge
时保留在原位,并在使用 writeFragment
时被覆盖?假设传递给 writeFragment
的新计算数据包含一个数组,其中包含未修改元素的浅表副本。
非常感谢你帮我解决这个问题!
与此同时,我实现了一个类似于 Apollo docs 中概述的合并功能。此函数尽可能多地重用 existing
数组中的项目,否则从 incoming
中获取项目。基于对象 id
检查相等性,它几乎总是出现在我的应用程序中。
这种方法唯一(次要)的缺点似乎是它不处理数组中的项目被更改的情况,但这种操作是在单独的突变中完成的,return 一个数组,无论如何。
为了后人,我的实现如下:
import type { FieldPolicy, Reference } from '@apollo/client'
/**
* Based on example in Apollo docs
*
* @see https://www.apollographql.com/docs/react/caching/cache-field-behavior/#merging-arrays-of-non-normalized-objects
*/
export const mergeArrayByIdFieldPolicy: FieldPolicy<Reference[]> = {
// eslint-disable-next-line @typescript-eslint/default-param-last
merge: (existing = [], incoming = [], { readField, mergeObjects }) => {
const merged = [...incoming]
const existingIds = existing.map((item) => readField<UUID>('id', item))
merged.forEach((item, index) => {
const itemId = readField<UUID>('id', item)
const existingIndex = existingIds.findIndex((id) => id === itemId)
if (existingIndex !== -1) {
merged[index] = mergeObjects(existing[existingIndex], merged[index])
}
})
return merged
},
}
我之前曾在 Apollo’s community chat 上问过这个问题,但由于两周后仍未得到答复,所以我将其重新发布在这里。
这个问题是关于Apollo 3.0引入的新缓存的。此更新弃用了本地查询,取而代之的是“类型策略”(或“字段策略”),其中包括几个用于使用缓存的工具,例如可以为每个 GraphQL 类型定义的 merge
函数来指定如何传入数据应与缓存中的现有数据合并。
我想知道哪些用例更适合 merge
函数,而不是通过 writeQuery
或 writeFragment
.
假设我有一个包含对象的数组,而这个数组是另一种类型的 属性。例如:
type Person {
hobbies: [Hobby!]!
}
该应用程序允许我删除、添加和更新此数组中的元素。 hobbies
应该使用 merge
函数吗?以前,我在本地突变解析器中编写了这个逻辑,但这些已被弃用。通过为这些解析器中的每一个定义一个函数,我可以很容易地模仿旧的行为,它包含 add/remove/update 逻辑,并使用 writeFragment
来存储结果。
我可以看到使用 merge
的好处,因为(我认为)它是一个较低的抽象层,但是它可以在传入 hobbies
数组的情况下使用吗?据我所知,它的工作方式意味着我们必须通过传入的输入来推断突变的类型。例如:
- 如果传入的数组少了一个元素(或者只包含了被删除的元素),我们做了一个删除操作,我们可以假设所有其他元素都保持不变。
- 如果传入的数组只包含一个元素,并且是新的,我们做了一个添加操作,并将这个元素合并到数组中
- 如果传入数组包含相同数量的元素(或只有一个更新的项目),我们必须更新一个并且现在必须找出要替换的一个(让我们假设,由于实现,它是无法直接更新单个
Hobby
,因为其他爱好也可能受到影响,例如由于非重叠时间限制)。
这似乎不如在调用 writeFragment
.
hobbies
的新值的旧方法优雅
使用 merge
有性能优势吗? hobbies
中的现有和不受影响的项目是否在使用 merge
时保留在原位,并在使用 writeFragment
时被覆盖?假设传递给 writeFragment
的新计算数据包含一个数组,其中包含未修改元素的浅表副本。
非常感谢你帮我解决这个问题!
与此同时,我实现了一个类似于 Apollo docs 中概述的合并功能。此函数尽可能多地重用 existing
数组中的项目,否则从 incoming
中获取项目。基于对象 id
检查相等性,它几乎总是出现在我的应用程序中。
这种方法唯一(次要)的缺点似乎是它不处理数组中的项目被更改的情况,但这种操作是在单独的突变中完成的,return 一个数组,无论如何。
为了后人,我的实现如下:
import type { FieldPolicy, Reference } from '@apollo/client'
/**
* Based on example in Apollo docs
*
* @see https://www.apollographql.com/docs/react/caching/cache-field-behavior/#merging-arrays-of-non-normalized-objects
*/
export const mergeArrayByIdFieldPolicy: FieldPolicy<Reference[]> = {
// eslint-disable-next-line @typescript-eslint/default-param-last
merge: (existing = [], incoming = [], { readField, mergeObjects }) => {
const merged = [...incoming]
const existingIds = existing.map((item) => readField<UUID>('id', item))
merged.forEach((item, index) => {
const itemId = readField<UUID>('id', item)
const existingIndex = existingIds.findIndex((id) => id === itemId)
if (existingIndex !== -1) {
merged[index] = mergeObjects(existing[existingIndex], merged[index])
}
})
return merged
},
}