【问题标题】:Apollo duplicates first result to every node in array of edgesApollo 将第一个结果复制到边数组中的每个节点
【发布时间】:2018-07-28 03:45:45
【问题描述】:

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

【问题讨论】:

    标签: apollo react-apollo


    【解决方案1】:

    发生这种情况的原因是因为您的数组中的项目在 Apollo 缓存中被“标准化”为相同的值。又名,它们在阿波罗看来是一样的。这通常是因为它们共享相同的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;
    }
    

    您的数组项属性会导致多个项返回相同的数据 ID。就我而言,多个项目有_id = null,这导致所有这些项目都被重复。当此函数返回 null 时,docs

    InMemoryCache 将回退到查询中对象的路径, 例如 ROOT_QUERY.allPeople.0 用于返回的第一条记录 allPeople 根查询。

    当我们的数组项不能很好地与defaultDataIdFromObject 配合使用时,这是我们真正想要的行为。

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

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

    【讨论】:

    • 更简洁地说,确保您没有在 graphql 中返回重复的 ID 对象。在我的情况下,这是我们的 ID 被保留为 NULL 的结果。
    【解决方案2】:

    把它放在你的 App.js 中

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

    【讨论】:

    • 您的意思是用括号括住函数体,以便将 dataIdFromObject 设置为三元运算符的选项之一?就目前而言,您不妨直接将 dataIdFromObject 设置为 undefined
    • @Simon 好点,我已经编辑了原始答案以反映您的建议
    • 嗨,我尝试了您的解决方案,但似乎 'IdGetterObj' 类型上不存在属性 'cursor' 。您还有其他建议来处理不能作为字段 'id' 或 '_id' 的对象吗?
    • 此解决方案不再有效,因为cursor 不再存在
    【解决方案3】:

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

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

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

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

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

    这些对象是 Apollo 尝试从中获取唯一 ID 的对象(您必须告诉 Apollo 对象中的哪个字段对于从 GraphQL 返回的“项目”数组中的每个对象是唯一的 - 与传递唯一当您在渲染 DOM 元素时使用“map”或其他迭代时,React 中“key”的值)。

    From Apollo dox:

    默认情况下,InMemoryCache 会尝试使用常见的 唯一标识符的 id 和 _id 的主键(如果存在) 以及对象上的 __typename。

    因此,请查看“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 对象是哪个领域即可。如果通过在查询定义中添加未获取,则添加它。

    【讨论】:

    • 很有帮助。谢谢! ?
    • 给读者的说明,同时dataIdFromObject has been deprecated 支持keyfields 对象
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-15
    相关资源
    最近更新 更多