具有嵌套查询结果的 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 条目。这基本上是正确的结果。但是,我使用 useQueryfetchMore 功能的组件列表在调用 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)。这就是导致我一开始感到困惑的原因。