Skip to content
This repository has been archived by the owner. It is now read-only.

Query returns undefined when same cache object is in multiple query responses #2678

Closed
irace opened this issue Dec 19, 2018 · 13 comments
Closed

Query returns undefined when same cache object is in multiple query responses #2678

irace opened this issue Dec 19, 2018 · 13 comments

Comments

@irace
Copy link

@irace irace commented Dec 19, 2018

I have two queries in components that are on screen at the same time – one (me) returns the current user, and another (group) returns all members of a group. Once cached, the cached results of each query look as follows:

ROOT_QUERY
  me: User
    User:bryan-irace
  group({"id": 1}): Group
    Group:some-group-name
      members: GroupMembers
        results: [User]
          0: User:bryan-irace
          1: User:kevin-grant

I am seeing some very strange behavior when the User model returned by the me query is also included in the members.results array returned by the group query. Specifically, the me query’s callback is invoked multiple times:

  1. data.me returned undefined (network request has not occurred and cache has not been populated yet)
  2. data.me returns the correct model (model is now stored in the cache)
  3. data.me returns undefined again, despite the model being present on disk

This third invocation is problematic – I don’t know why it is being triggered to begin with, but if it’s going to be triggered, data.me should certainly be populated.

I can fix this problem by either:

  1. Removing the groups query altogether
  2. Modifying the data such that the user returned by the me query is no longer a group member

Obviously neither of these are suitable workarounds, but in both cases, the third me query callback invocation does not occur.

Please advise 🙏

@irace
Copy link
Author

@irace irace commented Dec 19, 2018

If it helps, here are the two different variations on the same model, the first from the me query and the second from the group query:

"admin": true,
"firstName": "Bryan",
"lastName": "Irace",
"bio": "Push code too late on a Friday – what is wrong with me",
"professions": [{
  "id": 2,
  "name": "Software Developer",
  "__typename": "Profession"
}, {
  "id": 8,
  "name": "Polyglot Engineer42",
  "__typename": "Profession"
}],
"phoneNumber": "+15559274956",
"city": "Chicago, NY",
"email": "bryan@foo.bar",
"website": "https://irace.me",
"linkedinUrl": null,
"facebookUrl": null,
"instagramUrl": null,
"twitterUrl": null,
"googleUrl": null,
"yelpUrl": null,
"foursquareUrl": null,
"slug": "bryan-irace",
"tags": [],
"avatar": {
  "large": "https://some/avatar/url",
  "thumbnail": "https://some/avatar/url",
  "__typename": "UserAvatar"
}
"firstName": "Bryan",
"lastName": "Irace",
"professions": [{
  "name": "Software Developer",
  "__typename": "Profession"
}, {
  "name": "Polyglot Engineer42",
  "__typename": "Profession"
}],
"slug": "bryan-irace",
"avatar": {
  "large": "https://some/avatar/url",
  "thumbnail": "https://some/avatar/url",
  "__typename": "UserAvatar"
},
"tags": [],
"__typename": "User"
}
@sbrichardson
Copy link

@sbrichardson sbrichardson commented Dec 31, 2018

Is there an id field that you left off? Not having an id field will cause a lot of issues, since it's used by apollo when normalizing to the cache.

@irace
Copy link
Author

@irace irace commented Dec 31, 2018

@sbrichardson Thanks for getting back to me. In this case, the slug field is the unique identifier used for caching purposes.

@irace
Copy link
Author

@irace irace commented Jan 2, 2019

OK, I (mostly?) figured this out.

First, if caching was turned off, this bug did not manifest itself.

Ultimately, the issue is that I was making one query (modified for brevity) like:

user {
  slug
  profession {
    name
  }
}

And another query like:

user {
  slug
  profession {
    id
    name
  }
}

Even though the users were the same, and using slug as their cache ID, the fact that the nested professions were not being correctly identified as the same reference (due to one having an id property and the other not), the cache was in a weird state and incapable of realizing that the two users were – in fact – the same reference.

@irace
Copy link
Author

@irace irace commented Jan 2, 2019

This is a pretty easy mistake to make though, and the fact that this just returns undefined when the query’s data is visible in the cache via the Apollo inspector makes it really hard to figure out what's going on. No errors or anything.

@sbrichardson
Copy link

@sbrichardson sbrichardson commented Jan 4, 2019

Yes I've ran into that issue, was going to mention, typically it manifests if you remove an id field from a selection set accidentally, such as in a table UI, editing table columns to build a gql query dynamically.

Unfortnuately, this issue comes up and is resolved but the github issues are spread around in react-apollo, apollo-cache etc, so it compounds the troubleshooting effort.

https://www.apollographql.com/docs/react/advanced/caching.html#normalization

@irace
Copy link
Author

@irace irace commented Jan 4, 2019

@sbrichardson That documentation that you linked to doesn’t really give any indication that a missing cache key on a child object would prevent the parent object from being properly normalized. It seems like this should theoretically be something that Apollo can handle, or at the very least provide a more helpful error message?

@developdeez
Copy link

@developdeez developdeez commented Apr 26, 2019

Been stuck on this for hours. What's the fix?
It happens almost randomly it seems.

@Elijen
Copy link

@Elijen Elijen commented May 7, 2019

@developdeez I've fixed this by making sure id field is always selected for all nodes (might be nice to have this done automatically if omitting a field breaks the app). I am also now re-using the same query for components that request the same object. This kind of defeats the "only request what you really need" advantage of GraphQL, but is not a big deal for me.

@hwillson
Copy link
Member

@hwillson hwillson commented Jun 15, 2019

See the react-apollo 2.5.6 release notes for details around the returnPartialData prop. It will help here. Thanks!

@hwillson hwillson closed this Jun 15, 2019
@vfonic
Copy link

@vfonic vfonic commented Oct 10, 2019

@hwillson I don't think that returnPartialData resolves the issue.

returnPartialData provides data from the cache immediately.
Here the issue is different: after the query data is returned from the backend, instead of it being available in data property, the data property is undefined.

As @Elijen said (emphasis mine):

by making sure id field is always selected for all nodes (might be nice to have this done automatically if omitting a field breaks the app)

@deepikagunda
Copy link

@deepikagunda deepikagunda commented Nov 11, 2019

Seeing the same issue as @vfonic. Was on react-apollo 2.1.2 .Moved to 2.5.7 as well. Issue still present.

@vfonic
Copy link

@vfonic vfonic commented Nov 11, 2019

@deepikagunda try adding id field to your query. That fixed it for me.

query SomeQueryYouHave {
  posts {
    nodes {
      id
      # ... your other fields go here
    }
  }
}
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
7 participants