【发布时间】:2018-07-28 03:45:45
【问题描述】:
我正在使用 react-apollo 开发一个 react 应用程序
当我签入浏览器网络选项卡响应时通过graphql调用数据它显示数组的所有元素不同
但是我在我的应用程序中得到的或console.log() 然后数组的所有元素都与第一个元素相同。
我不知道如何解决请帮忙
【问题讨论】:
标签: apollo react-apollo
我正在使用 react-apollo 开发一个 react 应用程序
当我签入浏览器网络选项卡响应时通过graphql调用数据它显示数组的所有元素不同
但是我在我的应用程序中得到的或console.log() 然后数组的所有元素都与第一个元素相同。
我不知道如何解决请帮忙
【问题讨论】:
标签: apollo react-apollo
发生这种情况的原因是因为您的数组中的项目在 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),
})
});
【讨论】:
把它放在你的 App.js 中
cache: new InMemoryCache({
dataIdFromObject: o => o.id ? `${o.__typename}-${o.id}` : `${o.__typename}-${o.cursor}`,
})
【讨论】:
cursor 不再存在
我认为应避免使用其他两个答案中的方法,而应采用以下方法:
其实很简单。要了解它是如何工作的,只需按如下方式记录 obj:
dataIdFromObject: (obj) => {
let id = defaultDataIdFromObject(obj);
console.log('defaultDataIdFromObject OBJ ID', obj, id);
}
如果您遇到此问题,您将在日志中看到 id 将为空。
注意记录的“obj”。它将为每个返回的对象打印。
这些对象是 Apollo 尝试从中获取唯一 ID 的对象(您必须告诉 Apollo 对象中的哪个字段对于从 GraphQL 返回的“项目”数组中的每个对象是唯一的 - 与传递唯一当您在渲染 DOM 元素时使用“map”或其他迭代时,React 中“key”的值)。
默认情况下,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 对象