When I use client.resetStore() on logout, and the server returns an error for my current user query (as it should because their is no logged in user), Apollo throws an Uncaught (in promise) Error.
Note that if I reload the page instead of reset the store (or the current user query runs with no logged in user in the normal flow of the app), the server returns its error, but there is no Uncaught (in promise) Error.
Using: apollo-client 0.7.3, and react-apollo 0.8.1
Here's the stack trace:
Uncaught (in promise) Error: GraphQL error: User is not authorized to access path: viewer,user
at new ApolloError (apollo.umd.js:1498)
at apollo.umd.js:2722
ApolloError @ apollo.umd.js:1495
(anonymous) @ apollo.umd.js:2722
Promise.reject (async)
(anonymous) @ apollo.umd.js:2748
Promise (async)
(anonymous) @ apollo.umd.js:97
Promise (async)
HTTPFetchNetworkInterface.fetchFromRemoteEndpoint @ apollo.umd.js:81
(anonymous) @ apollo.umd.js:90
Promise.resolve (async)
HTTPFetchNetworkInterface.query @ apollo.umd.js:90
Deduplicator.query @ apollo.umd.js:1977
(anonymous) @ apollo.umd.js:2708
QueryManager.fetchRequest @ apollo.umd.js:2706
QueryManager.fetchQuery @ apollo.umd.js:2434
ObservableQuery.refetch @ apollo.umd.js:1613
(anonymous) @ apollo.umd.js:2510
QueryManager.resetStore @ apollo.umd.js:2507
ApolloClient.resetStore @ apollo.umd.js:2963
User._this.handleLogout @ User.js:11
@rafrex Thanks for reporting this issue!
@calebmer I think this issue probably isn't easy for someone who isn't very experienced and new to Apollo Client. You could make it easy by writing up a short diagnosis and one or two pointers for people who want to try fixing this with a PR 馃檪
Sure. When resetting the store in QueryManager#resetStore we re-fetch every query we know of. ObservableQuery#refetch returns a promise and I bet that is where are uncaught error is coming from 馃槈
To fix this just have QueryManager#resetStore return a promise that resolves when all of the queries have been re-fetched and rejects when one of the queries rejects.
Anyone watching the queries that error will be notified through their observer.error handle. That鈥檚 probably where the error should really be handled. This change just allows the user to prevent unhandled promise rejections.
Another option would be to just ignore all errors from refetch as the errors will likely be handled elsewhere, but I think returning a promise is a better choice as it also allows the user to know when the reset has completed.
@calebmer Is there a way to have it not refetch? For example when logging out, I sort of just want to kill everything. I'm running into issues where logging out -> logging in -> still has cached data from the logged out user.
Not sure if intentional, but I heard resetStore got some fixes recently, however I'm stuck on 0.5.21 due to the upgrading breaking updateQueries.
@booboothefool does the query when re-fetched contain data from the logged out user? If so, maybe look at the headers and what not being sent by the network interface to ensure that they too are being correctly reset.
I鈥檓 not sure with the nature of the resetStore fixes were, and I don鈥檛 see anything in the CHANGELOG.md after 0.5.21.
I'm with @booboothefool in that I would like an option to force it to not refetch (but I don't have the issue where cached data is returned_. In my case, I want to reset the store when a user logs out and go back to the login screen with a completely empty redux store and no memory of previously-run queries (including from the screen where they clicked "logout").
My server enforces authentication on most query types and I get an unhandled Promise rejection because the query on the current screen re-fetches after I clear out my auth token.
@PizzaBrandon the way to do that is to unmount all components that are currently holding on to queries, and then call resetStore(). In that case, no query will be refetched. It sounds like your login screen doesn't need any query, so this shouldn't be an issue with the current implementation.
@helfer I have a weird issue in which unmounting doesn't help.
Basically the app is trying to fetch a query on initial startup, but the server responds 401, since the token in localStorage is no longer valid. I detect the 401 in an afterware handler and trigger my logout procedures followed by a resetStore (before invoking next()).
I make sure to not reset the store before all graphql wrapped components are unmounted.
What seems to be happening is that both componentDidMount and componentWillRecieveProps (I don't really understand why yet, still reading the code) in react-apollo causes two separate observers to be added to the query, after my component is finished unmounting and resetStore is invoked one of the observers still seem to be left in this.observers on the ObservableQuery which in turn seems to prevent the query to be properly teared down when unmounting.
@jacobk is this in 0.11.x? We recently added a query recycling feature and may have overlooked store resetting.
@calebmer I tried both 0.11.x and older versions, same issue. I will see if I can reproduce the issue in a clean app.
Actually the underlying issue doesn't seem to be caused by resetStore. If I load the route (graphql component) and trigger the 401 network error and navigate away from the route in the afterware handler I can see that the query is still watched even though the component is no longer mounted (shows up in apollo chrome dev tools).
@calebmer @helfer here's a repo that cleanly reproduces the issue described above.
https://github.com/jacobk/apollo-issue-example
Just
git clone https://github.com/jacobk/apollo-issue-example
cd apollo-issue-example
yarn
npm start
It's based on react-boilerplate with only changes in this commit https://github.com/jacobk/apollo-issue-example/commit/a21980c580998f983dfd10b6002fa10e83bb1a71
Thanks for the reproducing error case @jacobk. This looks like a different issue could you either open a new ticket in apollo-client or react-apollo? Also, if you don鈥檛 mind could you reproduce your error with react-apollo-error-template? It鈥檚 very important that we be able to go into node_modules and edit the code to debug the error which it looks like react-boilerplate won鈥檛 let us do unless you can find the config option that will have react-boilerplate watch changes in node_modules.
@calebmer I've opened https://github.com/apollographql/apollo-client/issues/1319 and provided a new reproduce using react-apollo-error-template
Closing this in favor of #1319.
@calebmer I think I'm having another problem with this. I have a Channels query that returns results if the user is logged in, and throws if the user is logged out. When the user logs out, it refetches that query, which throws (as it should). But it seems like that query gets removed when it errors out (it's no longer in the ROOT_QUERY of the cache) -- so it doesn't get refetched when the user logs back in.
It's a bit unclear to me from the docs what the recommended pattern is here. Should unauthorized queries just return null? Should the client avoid mounting apollo containers that make unauthorized queries? Both of those approaches don't really seem ideal.
Nevermind, I discovered that I just need to use options: {errorPolicy: 'all'}.
The problem with using this to "cancel" requests is that reject is an error. If anything these requests need some form of isCancelled flag to ignore when the request resolves without producing an error.
Most helpful comment
@calebmer Is there a way to have it not refetch? For example when logging out, I sort of just want to kill everything. I'm running into issues where logging out -> logging in -> still has cached data from the logged out user.
Not sure if intentional, but I heard resetStore got some fixes recently, however I'm stuck on 0.5.21 due to the upgrading breaking updateQueries.