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

Apollo将第一个结果复制到边数组中的每个节点,apollo,react-apollo,Apollo,React Apollo,我正在使用react apollo开发react应用程序 通过graphql调用数据当我签入浏览器网络选项卡响应时,它会显示不同数组的所有元素 但我在我的应用程序中得到的是console.log(),然后数组的所有元素都与第一个元素相同。 我不知道如何修复,请帮我把这个放到你的App.js中 cache: new InMemoryCache({ dataIdFromObject: o => o.id ? `${o.__typename}-${o.id}` : `${o.__type

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

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

发生这种情况的原因是数组中的项在Apollo缓存中被“规范化”为相同的值。它们在阿波罗看来是一样的。这通常是因为它们共享相同的
符号(id)

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

默认情况下,Apollo cache运行此命令以进行规范化

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。在我的例子中,多个项具有
\u id=null
,这导致所有这些项重复。当此函数返回null时,表示

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

当数组项与
defaultDataIdFromObject
不兼容时,这就是我们实际想要的行为

因此,解决方案是使用传递给
Apollo客户机中
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)=>{
设id=defaultDataIdFromObject(obj);
log('defaultDataIdFromObject OBJ ID',OBJ,ID);
}
如果存在此问题,您将在日志中看到id为null

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

这些对象是Apollo试图从中获取唯一id的对象(您必须告诉Apollo,对于从GraphQL返回的“项”数组中的每个对象,对象中的哪个字段是唯一的,这与在呈现DOM元素时使用“映射”或其他迭代时在React中传递“键”的唯一值的方式相同)

:

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

因此,请查看“defaultDataIdFromObject”使用的已记录的“obj”-如果您没有看到“id”或“\u id”,则应在对象中提供每个对象唯一的字段

我将Apollo dox的示例更改为涵盖三种情况,即您可能提供了不正确的标识符—这适用于具有多个GraphQL类型的情况:

dataIdFromObject:(obj)=>{
设id=defaultDataIdFromObject(obj);
log('defaultDataIdFromObject OBJ ID',OBJ,ID);
如果(!id){
const{{uuuu typename:typename}=obj;
开关(类型名称){
“博客”案例:{
//如果您使用的不是'id'和'u id',在本例中为'blogId'
const undf=`${typename}:${obj.id}`;
const defined=`${typename}:${obj.blogId}`;
log('in Blogs-',未定义,已定义);
返回`${typename}:${obj.blogId}`;//返回'blogId',因为它是唯一的
//标识符。使用任何其他标识符将导致上述定义的问题。
}
案例“Post”:{
//若您使用的是散列键和排序键,那个么散列键不是唯一的。
//如果您在DB中进行查询,它将始终是相同的。
//如果您经常以DB进行扫描,它将是相同的值。
//因此,请同时使用哈希键和排序键来避免问题。
//使用这两种方法可以确保阿波罗使用的ID始终是唯一的。
//如果对于post,您使用的是blogID的hashKey和postd的sortKey
const notUniq=`${typename}:${obj.blogId}`;
const notUniq2=`${typename}:${obj.postId}`;
const uniq=`${typename}:${obj.blogId}${obj.postId}`;
console.log('in Post-',notUniq,notUniq2,uniq);
返回`${typename}:${obj.blogId}${obj.postId}`;
}
案例“评论”:{
//假设“注释”的标识符为“id”
//但你不会在你的应用程序中使用它,也不会从GraphQl获取数据,也就是说
//在GraphQL查询定义中省略了“id”。
const undend=`${typename}:${obj.id}`;
log('在注释中-',未定义);
//日志结果-空
//要修复它,只需在GraphQL定义中添加“id”。
返回`${typename}:${obj.id}`;
}
默认值:{
log(${typename}中的一个默认值不好,在单独的情况下定义它);
返回id;
}
我希望你现在看到其他两个答案中的方法是有风险的


您总是有唯一的标识符。只需让APOLLO知道它是对象中的哪个字段就可以了。如果没有通过在查询定义中添加来获取它,请添加它。

您的意思是用括号括住函数体,以便将dataIdFromObject设置为三元运算符的一个选项吗?就目前的情况而言,您还可以请将dataIdFromObject直接设置为undefined@Simon很好,我已经编辑了原始答案以反映您的建议您好,我尝试了您的解决方案,但类型“IdGetterObj”上似乎不存在属性“cursor”。您是否有其他建议来处理不能作为字段“id”或“_id”的对象?更简洁地说,请确保您没有返回graphql中的重复ID对象。在我的cas中