I have a query that returns data but results in an empty data object when using react-apollo.
The query contains 4 entities and if one of them is removed the resulting data object becomes normal.
I have replicated the issue in this simple sandbox https://codesandbox.io/embed/54rzlpzz9p
Try removing any entity from the query to data being populated.
P.S
I believe this issue is quite different from the related "empty data" issues
@timuric This is functioning as designed. Looking at your query:
query ProductDetails($id: ID!) {
product(id: $id) {
productType {
productAttributes {
id
values {
id
}
}
}
attributes {
attribute {
id
values {
slug
}
}
}
}
}
The first object returned in your productAttributes array, and the object returned by attributes, both have a __typename of Attribute and the same id of QXR0cmlidXRlOjEz, coming from your backend. This means as far as Apollo Client is concerned they are the same object. When they are stored in the cache, InMemoryCache normalization fires and stores them as one entity. Looking further at the data coming from your backend, the child values { id } and values { slug } selection sets also return the same objects, so it stands to reason that they can also be stored together when cache normalization fires. Since you haven't specified an id field in your second values { slug } selection set however, InMemoryCache creates an id; that id doesn't match the id used in the first values { id } selection set. So long story short, even though those values are really the same, InMemoryCache thinks they are different, and stores them separately in the cache. Then when future queries fire and try to pull data back out of the cache to fulfill the full query, those queries only find the values returned from the first values { id } selection set (which was properly associated with the normalized parent Attribute objects in the cache). Those results don't include the slug field though, so you've then hit what is known as a cache over-fetch issue - you're asking for fields that the cache doesn't know how to resolve, so it gives you an empty object back.
The good news is that to fix things you just have to add the id field to your second values selection set:
query ProductDetails($id: ID!) {
product(id: $id) {
productType {
productAttributes {
id
values {
id
}
}
}
attributes {
attribute {
id
values {
id
slug
}
}
}
}
}
This is kind of late but thanks @hwillson for that nice explanation and solution to this.
Most helpful comment
@timuric This is functioning as designed. Looking at your query:
The first object returned in your
productAttributesarray, and the object returned byattributes, both have a__typenameofAttributeand the sameidofQXR0cmlidXRlOjEz, coming from your backend. This means as far as Apollo Client is concerned they are the same object. When they are stored in the cache,InMemoryCachenormalization fires and stores them as one entity. Looking further at the data coming from your backend, the childvalues { id }andvalues { slug }selection sets also return the same objects, so it stands to reason that they can also be stored together when cache normalization fires. Since you haven't specified anidfield in your secondvalues { slug }selection set however,InMemoryCachecreates anid; thatiddoesn't match theidused in the firstvalues { id }selection set. So long story short, even though those values are really the same,InMemoryCachethinks they are different, and stores them separately in the cache. Then when future queries fire and try to pull data back out of the cache to fulfill the full query, those queries only find the values returned from the firstvalues { id }selection set (which was properly associated with the normalized parentAttributeobjects in the cache). Those results don't include theslugfield though, so you've then hit what is known as a cache over-fetch issue - you're asking for fields that the cache doesn't know how to resolve, so it gives you an empty object back.The good news is that to fix things you just have to add the
idfield to your secondvaluesselection set: