Apollo 将第一个结果复制到边缘数组中的每个节点

Apollo duplicates first result to every node in array of edges

我正在使用 react-apollo 开发一个 React 应用程序 当我检查浏览器网络选项卡响应时通过 graphql 调用数据它显示数组的所有元素不同 但是我在我的应用程序中得到或 console.log() 然后数组的所有元素与第一个元素相同。 我不知道如何解决请帮助

将此放入您的 App.js

cache: new InMemoryCache({
    dataIdFromObject: o => o.id ? `${o.__typename}-${o.id}` : `${o.__typename}-${o.cursor}`,
  })

发生这种情况的原因是数组中的项目 "normalized" 与 Apollo 缓存中的值相同。 AKA,它们在阿波罗看来是一样的。这通常是因为它们共享相同的 Symbol(id).

如果你打印出你的 Apollo 响应对象,你会注意到每个对象都有 Symbol(id),它被 Apollo 缓存使用。您的数组项可能具有相同的 Symbol(id),这会导致它们重复。为什么会这样?

默认情况下,Apollo 缓存运行此 function 进行规范化。

export function defaultDataIdFromObject(result: any): string | null {
  if (result.__typename) {
    if (result.id !== undefined) {
      return `${result.__typename}:${result.id}`;
    }
    if (result._id !== undefined) {
      return `${result.__typename}:${result._id}`;
    }
  }
  return null;
}

您的数组项目属性导致多个项目 return 相同的数据 ID。在我的例子中,多个项目有 _id = null 导致所有这些项目被重复。当这个函数 return 为 null 时 docs

InMemoryCache will fall back to the path to the object in the query, such as ROOT_QUERY.allPeople.0 for the first record returned on the allPeople root query.

当我们的数组项不能很好地与 defaultDataIdFromObject 一起工作时,这是我们真正想要的行为。

因此,解决方案是使用传递给 ApolloClient 中的 InMemoryCache 构造函数的 dataIdFromObject 选项手动配置这些唯一标识符。以下对我有用,因为我所有的对象都使用 _id 并且有 __typename.

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache({
    dataIdFromObject: o => (o._id ? `${o.__typename}:${o._id}`: null),
  })
});

我认为应避免使用其他两个答案中的方法,而应采用以下方法:

其实很简单。要了解它是如何工作的,只需按如下方式记录 obj:

dataIdFromObject: (obj) => {
   let id = defaultDataIdFromObject(obj);
   console.log('defaultDataIdFromObject OBJ ID', obj, id);
}

如果遇到此问题,您会在日志中看到 id 将为空。

注意记录'obj'。将为返回的每个对象打印它。

这些对象是 Apollo 试图从中获取唯一 ID 的对象(您必须告诉 Apollo 对象中的哪个字段对于从 GraphQL 返回的 'items' 数组中的每个对象都是唯一的 - 相同当您使用 'map' 或渲染 DOM 元素时的其他迭代时,您在 React 中为 'key' 传递唯一值的方式。

From Apollo dox:

By default, InMemoryCache will attempt to use the commonly found primary keys of id and _id for the unique identifier if they exist along with __typename on an object.

因此请查看 'defaultDataIdFromObject ' 使用的记录 'obj' - 如果您没有看到 'id' 或“_id”,那么您应该在您的对象中提供唯一的字段每个对象。

我将示例从 Apollo dox 更改为涵盖您可能提供了不正确标识符的三种情况 - 它适用于您有不止一种 GraphQL 类型的情况:

dataIdFromObject: (obj) => {
   let id = defaultDataIdFromObject(obj);
   console.log('defaultDataIdFromObject OBJ ID', obj, id);

   if (!id) {
     const { __typename: typename } = obj;
     switch (typename) {
       case 'Blog': {
      // if you are using other than 'id' and '_id' - 'blogId' in this case
         const undef = `${typename}:${obj.id}`;
         const defined = `${typename}:${obj.blogId}`;
         console.log('in Blogs -', undef, defined);
         return `${typename}:${obj.blogId}`; // return 'blogId' as it is a unique
     //identifier. Using any other identifier will lead to above defined problem.
       }
       case 'Post': {
       // if you are using hash key and sort key then hash key is not unique.
       // If you do query in DB it will always be the same.
       // If you do scan in DB quite often it will be the same value. 
       // So use both hash key and sort key instead to avoid the problem.
       // Using both ensures ID used by Apollo is always unique.
       // If for post you are using hashKey of blogID and sortKey of postId
          const notUniq = `${typename}:${obj.blogId}`;
          const notUniq2 = `${typename}:${obj.postId}`;
          const uniq = `${typename}:${obj.blogId}${obj.postId}`;
          console.log('in Post -',  notUniq, notUniq2, uniq);
          return `${typename}:${obj.blogId}${obj.postId}`;
       }
       case 'Comment': {
         // lets assume 'comment's identifier is 'id'
         // but you dont use it in your app and do not fetch from GraphQl, that is
         // you omitted 'id' in your GraphQL query definition.
         const undefnd = `${typename}:${obj.id}`;
         console.log('in Comment -', undefnd);  
         // log result - null
         // to fix it simply add 'id' in your GraphQL definition.
         return `${typename}:${obj.id}`;
       }
       default: {
         console.log('one falling to default-not good-define this in separate Case', ${typename});
         return id;
       }

我希望你现在看到其他两个答案中的方法是有风险的。

您始终拥有唯一的标识符。简单地帮助 APOLLO 知道它在对象中的哪个字段。如果它不是通过在查询定义中添加来获取的,请添加它。