具有嵌套查询结果的 ApolloClient v3 fetchMore
ApolloClient v3 fetchMore with nested query results
我正在使用 ApolloClient 3 GitHub GraphQL API 从存储库中检索所有版本。
查询如下所示:
query ($owner: String!, $name: String!, $first: Int, $after: String, $before: String) {
repository(owner: $owner, name: $name) {
id
releases(orderBy: {field: CREATED_AT, direction: DESC}, first: $first, after: $after, before: $before) {
nodes {
name
publishedAt
resourcePath
tagName
url
id
isPrerelease
description
descriptionHTML
}
totalCount
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
}
结果负载如下所示:
这 returns 我的前 x 个条目 (nodes
)。到目前为止,一切都很好。
我需要实现分页,我利用了 ApolloClient useQuery
提供的 fetchMore
功能。调用 fetchMore
成功获取下 x 个条目,但这些条目未显示在我的组件列表中。
根据ApolloClient Pagination documentation,似乎有必要用ApolloClient缓存机制来处理fetchMore
结果的merging
。该文档对于简单的情况是可以理解的,但我正在努力为需要合并在一起的实际结果数组深深嵌套在查询结果中的情况实施解决方案 (repository -> releases -> nodes
)。
这是我对 InMemoryCache 选项合并的实现:
const inMemoryCacheOptions = {
addTypename: true,
typePolicies: {
ReleaseConnection: {
fields: {
nodes: {
merge(existing, incoming, options) {
const previous = existing || []
const results = [...previous, ...incoming]
return results
}
}
}
},
}
}
此处的 results
数组包含完整列表,包括现有条目和新的 x 条目。这基本上是正确的结果。但是,我使用 useQuery
和 fetchMore
功能的组件列表在调用 fetchMore
后没有获得新条目。
我在上面的 inMemoryCacheOptions
代码中尝试了各种组合,但到目前为止我都没有成功。
要添加更多上下文,这是相关的组件代码:
export default function Releases() {
const { loading, error, data, fetchMore } = useQuery(releasesQuery, {
variables: {
owner: "theowner",
name: "myrepo",
first: 15
}
});
if (loading) return null;
if (error) {
console.error(error);
return null;
}
if (data) {
console.log(data?.repository?.releases?.pageInfo?.endCursor);
}
const handleFetchMore = () => {
fetchMore({
variables: {
first: 15,
after: data?.repository?.releases?.pageInfo?.endCursor
}
});
};
return (
<div>
<ul>
{data?.repository?.releases?.nodes?.map(release => (
<li key={release.id}>{release.name}</li>
))}
</ul>
<button onClick={handleFetchMore}>Fetch More</button>
</div>
);
}
在 fetchMore
之后,组件不会使用新数据重新呈现。
如果有人有任何其他想法可以尝试,我将不胜感激。
我终于设法解决了这个问题。反应组件代码没有变化,但 InMemoryCacheOptions
现在看起来像这样:
const inMemoryCacheOptions = {
addTypename: true,
typePolicies: {
Repository: {
fields: {
releases: {
keyArgs: false,
merge(existing, incoming) {
if (!incoming) return existing;
if (!existing) return incoming;
const { nodes, ...rest } = incoming;
// We only need to merge the nodes array.
// The rest of the fields (pagination) should always be overwritten by incoming
let result = rest;
result.nodes = [...existing.nodes, ...nodes];
return result;
}
}
}
}
}
};
我的原始代码的主要变化是我现在为 Repository
类型的 releases
字段定义了 typePolicy。以前我试图直接进入 Release
类型的 nodes
字段。由于我的 Repository
键入了 gql 查询的根并在组件中使用,它现在从缓存中读取合并结果。
如果我像文档中提到的那样为 Query
指定了类型策略,我将无法为 releases
字段指定合并行为,因为它太深了一层(即Query -> repository -> releases
)。这就是导致我一开始感到困惑的原因。
我正在使用 ApolloClient 3 GitHub GraphQL API 从存储库中检索所有版本。
查询如下所示:
query ($owner: String!, $name: String!, $first: Int, $after: String, $before: String) {
repository(owner: $owner, name: $name) {
id
releases(orderBy: {field: CREATED_AT, direction: DESC}, first: $first, after: $after, before: $before) {
nodes {
name
publishedAt
resourcePath
tagName
url
id
isPrerelease
description
descriptionHTML
}
totalCount
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
}
结果负载如下所示:
这 returns 我的前 x 个条目 (nodes
)。到目前为止,一切都很好。
我需要实现分页,我利用了 ApolloClient useQuery
提供的 fetchMore
功能。调用 fetchMore
成功获取下 x 个条目,但这些条目未显示在我的组件列表中。
根据ApolloClient Pagination documentation,似乎有必要用ApolloClient缓存机制来处理fetchMore
结果的merging
。该文档对于简单的情况是可以理解的,但我正在努力为需要合并在一起的实际结果数组深深嵌套在查询结果中的情况实施解决方案 (repository -> releases -> nodes
)。
这是我对 InMemoryCache 选项合并的实现:
const inMemoryCacheOptions = {
addTypename: true,
typePolicies: {
ReleaseConnection: {
fields: {
nodes: {
merge(existing, incoming, options) {
const previous = existing || []
const results = [...previous, ...incoming]
return results
}
}
}
},
}
}
此处的 results
数组包含完整列表,包括现有条目和新的 x 条目。这基本上是正确的结果。但是,我使用 useQuery
和 fetchMore
功能的组件列表在调用 fetchMore
后没有获得新条目。
我在上面的 inMemoryCacheOptions
代码中尝试了各种组合,但到目前为止我都没有成功。
要添加更多上下文,这是相关的组件代码:
export default function Releases() {
const { loading, error, data, fetchMore } = useQuery(releasesQuery, {
variables: {
owner: "theowner",
name: "myrepo",
first: 15
}
});
if (loading) return null;
if (error) {
console.error(error);
return null;
}
if (data) {
console.log(data?.repository?.releases?.pageInfo?.endCursor);
}
const handleFetchMore = () => {
fetchMore({
variables: {
first: 15,
after: data?.repository?.releases?.pageInfo?.endCursor
}
});
};
return (
<div>
<ul>
{data?.repository?.releases?.nodes?.map(release => (
<li key={release.id}>{release.name}</li>
))}
</ul>
<button onClick={handleFetchMore}>Fetch More</button>
</div>
);
}
在 fetchMore
之后,组件不会使用新数据重新呈现。
如果有人有任何其他想法可以尝试,我将不胜感激。
我终于设法解决了这个问题。反应组件代码没有变化,但 InMemoryCacheOptions
现在看起来像这样:
const inMemoryCacheOptions = {
addTypename: true,
typePolicies: {
Repository: {
fields: {
releases: {
keyArgs: false,
merge(existing, incoming) {
if (!incoming) return existing;
if (!existing) return incoming;
const { nodes, ...rest } = incoming;
// We only need to merge the nodes array.
// The rest of the fields (pagination) should always be overwritten by incoming
let result = rest;
result.nodes = [...existing.nodes, ...nodes];
return result;
}
}
}
}
}
};
我的原始代码的主要变化是我现在为 Repository
类型的 releases
字段定义了 typePolicy。以前我试图直接进入 Release
类型的 nodes
字段。由于我的 Repository
键入了 gql 查询的根并在组件中使用,它现在从缓存中读取合并结果。
如果我像文档中提到的那样为 Query
指定了类型策略,我将无法为 releases
字段指定合并行为,因为它太深了一层(即Query -> repository -> releases
)。这就是导致我一开始感到困惑的原因。