This is an odd bug, and I couldn't find any issues specifically related to it that are currently open. This is a tough one to reproduce as well, but we were going through an upgrade to 2.1.9 where one of our components reliably had this problem every single time.
The bug is basically that <Query> will render children(queryResult) where that queryResult is a currently fetching query, that has some fields already in the InMemoryCache. Below, I'll try and set up the component structure so you can see how this happens, and the general call trace that leads to it happening.
Reproduction Setup
A is a parent of component B, both are using <Query> to render their own GQL queriesB's query has all of the fields of A, plus some extra. Effectively B's query is a superset of A's.fetchPolicy is not defined, so both queries use the default cache-only<Query>.The gql for both are structured like so:
query A {
user {
id
name
}
}
query B {
user {
id
name
birthDay
}
}
Intended outcome:
In render(), I expect that the data argument used in B's <Query> render function to have loading set to true, and to not have a user field until loading switches to false and B's query is fully satisfied.
Actual outcome:
data looks like so:
{
loading: true,
networkStatus: 1,
user: {
id: '1',
name: 'Bob',
},
...
}
As you can see, the data prop is returning partially cached data for the query. This makes it hard to guarantee when any of the actual data props are safe to use short of just defaulting to rendering nothing when loading. This is not always an optimal UX. Being able to safely use data.user when it is there AND loading is true will prevent devs from having to check networkStatus and all subfields throughout the query to be able to make safe assumptions about the shape of their query result.
Call Trace
Basically what's happening is this:
A is mounting BB: componentDidMount() is called first, and its query begins fetchingA: componentDidMount() is called, and its query begins fetchingA: the query is fulfilled first, and after serveral subscription's next calls, will eventually call this.updateCurrentData() inside of the mounted <Query>B: this triggers a render cycle for B since it is a child of A, where calling this.getQueryResult() inside of render() results in partially-filled dataB: children(queryResult) is called with the partially-filled and still loading result.Version
I've also had something similar happen
query listRepositories($offset: Int) {
allRepositoriesList(orderBy: CREATED_AT_DESC, first: 20, offset: $offset) {
createdAt
id
name
refId
updatedAt
}
}
then when I want to use fetch more I have to check if allRepositoriesList is an array even though, by default it SHOULD be an array, but, its an object.
return fetchMore({
variables: {
offset: (data.allRepositoriesList || []).length // definitely a bug
},
updateQuery: (prev, { fetchMoreResult }) =>
!fetchMoreResult
? prev
: {
...prev,
allRepositoriesList: [
...prev.allRepositoriesList,
...fetchMoreResult.allRepositoriesList
]
}
});
and when I pull data out of the query, I'm doing it as follows so even the default is an Array. Apollo is doing something crazy.
{({data = {allRepositoriesList: []}) => ...}
I have seen this too and when I use cachePolicy: "cache-and-network I can reproduce it every time in our app. In my case this happens when I use apollo-link-state and nested content:
query Query {
parent @client {
id
name
child @client {
id
name
}
}
}
If parent is already in the cache the query returns parent object without child field. If I change the fetchPolicy to cache-first it returns data correctly (after it has received child data). I can't reproduce this if I just try to render the query component. I think @benjaminsaurusrex explanation is exactly what causes this.
Here is a small repro case: https://codesandbox.io/s/wn1v90qjl7
I have an understanding that query should never return partial data.
This PR seems to fix this issue: https://github.com/apollographql/apollo-client/pull/3956
@jsslai I can confirm that apollographql/apollo-client#3956 _will_ fix the issue. I even have a unit test case for <Query /> that proves so. I will open a PR with the test case as soon as the next release of apollo-cache-inmemory is cut. Hopefully that happens soon as this is a very irksome issue!
Awesome, I'll close this issue then. 馃憤
This exact scenario still happens for me with [email protected]
Most helpful comment
This exact scenario still happens for me with [email protected]