Nested objects with no ids and different selection sets returns no data
See original GitHub issueIntended outcome: With returnPartialData false, requesting non-matching selection sets from nested, denormalized objects (with no id), should alert me that there is data loss, and therefore complete data can never be achieved.
In this case, our User has a set of Preferences
, and under preferences there are CalendarPreferences
. Neither Preferences nor CalendarPreferences has their own id.
Take this very contrived query (I promise there’s a realistic query with the same symptoms)
query {
userWithPreferenceOne: user(id: 1) {
id
preferences {
calendarPreferences {
preferenceOne
}
}
userWithPreferenceTwo: user(id: 1) {
id
preferences {
calendarPreferences {
preferenceTwo
}
}
}
}
Apollo Client should alert me that because there is no custom merge function defined for Preferences or CalendarPreferences, and because there are no ids, it won’t merge them, and therefore either userWithPreferenceOne or userWithPreferenceTwo will never have complete data.
At the very least, warnAboutDataLoss should fire, however in a case like this where data will never resolve, an error seems appropriate.
Actual outcome:
When a query like the one above is made using the useQuery
hook. The results are
error: undefined,
data: undefined,
loading: false
There are no errors or warnings logged, so there is no indication that anything failed. However the query is ‘finished’ and yet there is no data and no error.
With returnPartialData
on, the incomplete data is returned (turning this on is undesirable, we need all the data).
Turning on partialRefetch
leads to an infinite loop of trying to load the complete data and failing.
I stepped through the warnAboutDataLoss
function and the problematic object (preferences, in the above example) early returns on this condition: https://github.com/apollographql/apollo-client/blob/a975320528d314a1b7eba131b97d045d940596d7/src/cache/inmemory/writeToStore.ts#L362-L365
Since it is only checking top level properties, it verifies that ‘existing’ and ‘incoming’ both have ‘calendarPreferences’ fields, but it does not catch that one only has preferenceOne and one only has preferenceTwo.
How to reproduce the issue: I have created a reproduction here: https://github.com/ford-outreach/react-apollo-error-template Here is the diff (apologies that it’s a little noisy, prettier went to town): https://github.com/apollographql/react-apollo-error-template/compare/master...ford-outreach:master
Versions 3.0.2 in our app 3.2.5 in the reproduction
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:11 (2 by maintainers)
Top GitHub Comments
@benjamn FYI we had a variation of this (actually an infinite loop of queries, not data loss) that, oddly enough went away in 3.4.0-beta.4 (must be due do that cache canonicalization? I don’t see any other commits in there since 3.3.6…).
In our case, for 3.3.6, we had two queries, where one was getting only
keyA
from avalueObject
:And separate query on the same page was getting both
keyA
andkeyB
:(Note that our
valueObject
’s keys actually point toid
-ized entities, which I think is different from the OP’s schema.)In our case, these two GraphQL queries were invalidating each other, and the
QueryInfo.setDiff
breakpoint’sdiff
/oldDiff
was basically showing:I.e. one query was causing
keyB
to beundefined
(probably the one that didn’t call it) and the other query was causingkeyB
to benull
(which it was validlynull
from our backend response).This diff wasn’t causing our react components to re-render, however it was causing an ~infinite loop in the cache where each query would take turns invalidating / refetching the other. Here’s the Initiator stack trace from one of the ~10s/100s of
graphql
requests in the network tab:So, odd things:
Not sure why in the OP case, it caused data loss, but for us caused an infinite loop of queries, and
(Actually maybe this is just b/c we have multiple queries on the same page touching the same
anEntity(sameId) { valueObject }
but with different projections.)Not sure why 3.4.0-beta.4 “fixed” it … i.e. we saw no looping of queries … is this good/bad/expected?
I.e. is 3.4.0-beta.4 is merging value objects when it’s not supposed to? Or maybe we got lucky and the fact that our diff was “just”
keyB: null
vs.keyB: undefined
, the canonicalization now threats those as “good enough” the same?I dunno, maybe this is not a super-helpful update, b/c basically we stayed on 3.3.6 and are using
ValueObject: { merge: true }
in our type policies to fix it, but I nonetheless just wanted to post the FYI that 3.4.0 has a change in behavior here, such that we originally thought “oh this was fixed in 3.4.0-beta.4 and we should just upgrade” … but actually the real fix is to setmerge: true
, and hence the mystery/question if 3.4.0 caused this change/fix on purpose or not.Thanks!
@leethree, ah, thanks for pointing that out!