Offline mutations are not being paused
Describe the bug
Hi there, first off - want to say thank you for providing this wonderful library. It's been wonderful in development.
In my react native app - I am attempting to use the offline mutation persistence features of v4 (https://tanstack.com/query/v4/docs/guides/mutations#persisting-offline-mutations) however - it seems like, when a device goes offline (whether before or during a mutation), the state of the mutation is not paused but rather immediately errors out which means it is not stored my the persist query client and cannot be resumed at hydration.
Here is my config:
const queryClient = new QueryClient({
defaultOptions: {
mutations: {
cacheTime: STARTING_TIME * 60 * 60 * 24, // 24 hours
},
},
})
const asyncStoragePersister = createAsyncStoragePersister({
storage: AsyncStorage,
throttleTime: 1000,
})
const persistOptions: PersistQueryClientProviderProps["persistOptions"] = {
persister: asyncStoragePersister,
dehydrateOptions: {
dehydrateMutations: true,
dehydrateQueries: false
},
maxAge: Infinity,
}
const onRestoreSuccess = async () => {
queryClient.resumePausedMutations()
}
queryClient.setMutationDefaults([MUTATION_KEY], {
mutationFn: async (mutation) => {
// ... api call
},
onError(error, variables, context) {
// Toast to show error
Toast.show({ type: "error", text1: error.message || "mutation error!" })
},
onSettled(data, error, variables, context) {},
onSuccess(data, variables, context) {},
})
// App
export default function App() {
return (
<PersistQueryClientProvider client={queryClient} persistOptions={persistOptions} onSuccess={onRestoreSuccess}>
// ... children
</PersistQueryClientProvider>
)
}
// Some component that calls default mutation
const { mutate } = useMutation([MUTATION_KEY])
mutate(payload)
This shows a toast of No Internet Connection every time... is there a config variable i am missing to get offline mutations to pause?
Your minimal, reproducible example
N/A
Steps to reproduce
- Configure queryClient & PersistQueryClientProvider as outlined is description
- Turn device offline
- invoke default mutation
Expected behavior
offline mutations should be paused and dehydrated into persister - which allows them to be resumed via queryClient.resumePausedMutations()
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
React Native
react-query version
v4.0.10
TypeScript version
No response
Additional context
No response
The offline example shows how to do this and it works. If it doesn't work for you, you'd have to provide your own reproduction that doesn't work https://tanstack.com/query/v4/docs/examples/react/offline
I have almost the same issue. I want to resend failed mutations with resumePausedMutations(), but I can't find a way to cache mutations with isPaused: true on error
Are there any examples of how to do that?
yes, this is the working example: https://tanstack.com/query/v4/docs/examples/react/offline
if you find something that is not working in this example, let me know.
@TkDodo The example you sent is caching mutations with isPaused: true only when there is no internet connection. When mutation finishes with error (event network error), it is cached with isPaused: false and resumePausedMutations() doesn't resend them
@pavelbabenko yes because the mutation finished in an erroneous state and is thus not "resumable" anymore. It could've also failed with a 500 error; Once a query is in error state, it is "done" from a fetching point of view.
The most likely scenario where I can see this happen is when you have connection, then start the mutation, then the connection goes away while you were doing the mutation? Otherwise, if you are already offline, it shouldn't even have started with networkMode: 'online'.
The best way to handle this scenario is to set retry: 1 or to a function that performs a retry if there is a NetworkError. In that case, if the mutation fails, it will be paused before the retry happens, thus it is also resumable with resumePausedMutations.
I've recently implemented this myself and since I'm pretty new to React Native I also had some trouble getting it all working. I've managed to get it working now and created an example repo hopefully this can help someone else.
I had some issues with Android / iPhone simulators not reconnecting back to the internet properly (causing mutations to retrigger but getting a network error), and I also experienced some persistance issues (atleast on Android). So I would recommend testing on a physical device.
@fedorish your example repo looks really good 👏
one thing that would potentially correlate to your issues is that you re-create the QueryClient and the persister during rendering (code pointer).
So if App re-renders, you'll get a new QueryClient and thus a new cache, and a new persister ...
You'd want to either create these things outside of the App component, or inside of a useState lazy initializer. I have a blogpost on this topic:
- https://tkdodo.eu/blog/use-state-for-one-time-initializations
@TkDodo
Thank you! 🙏🏼
Also thank you for the suggestion, now when you mention it, it's so obvious 😅 I think it's outside of App.js in the official examples aswell, so not sure how I managed to miss that. I've pushed a fix! 👍🏽
This seems to have fixed the Android persistance issue, but iPhone simulators still gives network errors. The mutation runs and it passes the variables correctly, but it errors with Network request failed, and I think I read somewhere that there is this issue with iPhone simulators having issues with connecting back to the internet.
I'm not sure how they work but I assume the device sends a signal that it's connected to the internet before its "fully connected" and therefore network requests fails.