Intended outcome:
For a Query with fetchPolicy: no-cache
When I call fetchMore
And I provide an updateQuery
function to merge in new results
I expect the Query to re-render
With the new results available in data
Actual outcome:
However the component does not re-render with new results.
If I remove the fetchPolicy: no-cache
,
The component re-renders with new data as expected when fetchMore
is called.
How to reproduce the issue:
https://codesandbox.io/embed/apollo-client-error-template-gfc7l
With fetchPolicy: cache-first
, clicking "Fetch more" adds "Another Person" to the list.
With fetchPolicy: no-cache
, clicking "Fetch more" does nothing.
Versions
The above codesandbox.io includes its own list of versions.
But the issue also shows up in our app code on my local machine, which has the following versions:
System:
OS: macOS 10.14.5
Binaries:
Node: 8.11.3 - ~/.nvm/versions/node/v8.11.3/bin/node
Yarn: 1.17.3 - ~/Projects/wonderschool/mobile/node_modules/.bin/yarn
npm: 5.6.0 - ~/.nvm/versions/node/v8.11.3/bin/npm
Browsers:
Chrome: 76.0.3809.100
Firefox: 66.0.3
Safari: 12.1.1
npmPackages:
apollo: ^2.16.0 => 2.16.0
apollo-boost: ^0.4.4 => 0.4.4
react-apollo: ^3.0.1 => 3.0.1
Problem:
By specifying 'no-cache', the ui components (useQuery hooks, Query components, etc.) are not subscribing to cache updates to that particular query.. thus, the UI is not updating.
Solution:
I'd suggest using the 'network-only' fetch policy. It is essentially the same, with the _exception_ that the results from the query are actually written to the cache. If there's a reason why this fetch policy doesn't work in your case, let me know.
The code from the Apollo Client that causes his behavior.
// set up a watcher to listen to cache updates
const cancel = fetchPolicy !== 'no-cache'
? this.updateQueryWatch(queryId, query, options)
: undefined;
@chaunceyau There is a case i'm trying to solve. We have a search screen that return 20 large objects and it also have a pagination. Because of this cache write, after some pagination, results start to take a long time to be displayed. For example, after 10 pages, it takes about 20 seconds to be displayed after the query is resolved, and all subsequent queries starts to take this time.
My first try was to change this query to no-cache, but I faced the same problem described here. Since updateQuery doesnt work for no-cache, I cannot display more results.
At this point, my idea is to handle all new results with react's state.
Let me know if there is a better approach
I am having the exactly same problem. Cache is slow for me and "no-cache" breaks pagination 馃し
Same issue here, network-only
isn't viable as the results are too large.
@hwillson Sorry for the ping, but would appreciate input from a contributor here; workarounds, fixes or estimations on how complex this would be to solve. If it's somewhat simple I'd be happy to take a look.
I did some digging and it appears fetchMore
will always cache whatever is returned from updateQuery
, even when no-cache
is applied.
When no-cache
is used QueryManager.markQueryResult
updates the results via QueryManager.setQuery()
instead of cache.write()
. I'm guessing this is how uncached data is supposed to be handled.
ObservableQuery.fetchMore()
calls ObservableQuery.updateQuery()
, which only uses cache.write()
however:
I attempted to fix this via the following patch, but wasn't able to figure out how to re-render the component since updateQueryWatch
isn't being called. It also accesses the private queryManager.setQuery()
function externally.
diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts
index 30c805e4..f19adff7 100644
--- a/src/core/ObservableQuery.ts
+++ b/src/core/ObservableQuery.ts
@@ -301,6 +301,7 @@ export class ObservableQuery<
fetchMoreOptions: FetchMoreQueryOptions<TVariables, K> &
FetchMoreOptions<TData, TVariables>,
): Promise<ApolloQueryResult<TData>> {
+ const { fetchPolicy } = this.options;
const combinedOptions = {
...(fetchMoreOptions.query ? fetchMoreOptions : {
...this.options,
@@ -310,7 +311,7 @@ export class ObservableQuery<
...fetchMoreOptions.variables,
},
}),
- fetchPolicy: 'network-only',
+ fetchPolicy === 'no-cache' ? fetchPolicy : 'network-only',
} as WatchQueryOptions;
const qid = this.queryManager.generateQueryId();
@@ -331,7 +332,7 @@ export class ObservableQuery<
fetchMoreResult: data,
variables: combinedOptions.variables as TVariables,
}) : data;
- });
+ }, fetchPolicy === 'no-cache');
this.queryManager.stopQuery(qid);
return fetchMoreResult as ApolloQueryResult<TData>;
},
@@ -488,6 +489,7 @@ export class ObservableQuery<
previousQueryResult: TData,
options: UpdateQueryOptions<TVars>,
) => TData,
+ noCache: boolean = false,
): void {
const { queryManager } = this;
const {
@@ -503,15 +505,22 @@ export class ObservableQuery<
);
if (newResult) {
- queryManager.cache.write({
- query: document,
- result: newResult,
- dataId: 'ROOT_QUERY',
- variables,
- });
+ if (noCache) {
+ queryManager.setQuery(this.queryId, () => ({
+ newData: { result: newResult, invalidated: true, complete: true },
+ }));
+ } else {
+ queryManager.cache.write({
+ query: document,
+ result: newResult,
+ dataId: 'ROOT_QUERY',
+ variables: variables,
+ });
+ }
queryManager.broadcastQueries();
}
+
}
public stopPolling() {
Any kind of feedback and help would be appreciated 馃憤
We can create new state variable and collect items in it as workaround for older versions of apollo-client
const [usersList, setUsersList] = useState([])
const [nextToken, setNextToken] = useState(undefined)
const { data: { list } = {}, fetchMore } = useQuery(gql(GET_LIST), {
fetchPolicy: "no-cache",
skip: !!list,
onCompleted: (data) => {
setNextToken(data.list.nextToken)
setUsersList(data.list.items)
},
})
const loadMore = async () => {
if (!nextToken) {
return null
}
return await fetchMore({
updateQuery: (previousResult, { fetchMoreResult, variables }) => {
setNextToken(fetchMoreResult.list.nextToken)
setUsersList([...usersList, ...fetchMoreResult.list.items])
return null
},
})
}
Please fix this.
I think this is not a bug, if you use fetchMore with updateQuery, there is no cache to update, because of choose such policy.
So there is 2 ways for now
Most helpful comment
Problem:
By specifying 'no-cache', the ui components (useQuery hooks, Query components, etc.) are not subscribing to cache updates to that particular query.. thus, the UI is not updating.
Solution:
I'd suggest using the 'network-only' fetch policy. It is essentially the same, with the _exception_ that the results from the query are actually written to the cache. If there's a reason why this fetch policy doesn't work in your case, let me know.
The code from the Apollo Client that causes his behavior.
// set up a watcher to listen to cache updates const cancel = fetchPolicy !== 'no-cache' ? this.updateQueryWatch(queryId, query, options) : undefined;