diff --git a/src/core/mutation.ts b/src/core/mutation.ts
index aeb1942a98..37c514a917 100644
--- a/src/core/mutation.ts
+++ b/src/core/mutation.ts
@@ -4,7 +4,7 @@ import type { MutationObserver } from './mutationObserver'
import { getLogger } from './logger'
import { notifyManager } from './notifyManager'
import { Removable } from './removable'
-import { Retryer } from './retryer'
+import { canFetch, Retryer } from './retryer'
import { noop } from './utils'
// TYPES
@@ -330,7 +330,7 @@ export class Mutation<
context: action.context,
data: undefined,
error: null,
- isPaused: false,
+ isPaused: !canFetch(this.options.networkMode),
status: 'loading',
variables: action.variables,
}
diff --git a/src/reactjs/tests/useMutation.test.tsx b/src/reactjs/tests/useMutation.test.tsx
index 799be48033..cae6d06d4f 100644
--- a/src/reactjs/tests/useMutation.test.tsx
+++ b/src/reactjs/tests/useMutation.test.tsx
@@ -445,6 +445,101 @@ describe('useMutation', () => {
onlineMock.mockRestore()
})
+ it('should call onMutate even if paused', async () => {
+ const onlineMock = mockNavigatorOnLine(false)
+ const onMutate = jest.fn()
+ let count = 0
+
+ function Page() {
+ const mutation = useMutation(
+ async (_text: string) => {
+ count++
+ await sleep(10)
+ return count
+ },
+ {
+ onMutate,
+ }
+ )
+
+ return (
+
+
+
+ data: {mutation.data ?? 'null'}, status: {mutation.status},
+ isPaused: {String(mutation.isPaused)}
+
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ await rendered.findByText('data: null, status: idle, isPaused: false')
+
+ rendered.getByRole('button', { name: /mutate/i }).click()
+
+ await rendered.findByText('data: null, status: loading, isPaused: true')
+
+ expect(onMutate).toHaveBeenCalledTimes(1)
+ expect(onMutate).toHaveBeenCalledWith('todo')
+
+ onlineMock.mockReturnValue(true)
+ window.dispatchEvent(new Event('online'))
+
+ await rendered.findByText('data: 1, status: success, isPaused: false')
+
+ expect(onMutate).toHaveBeenCalledTimes(1)
+ expect(count).toBe(1)
+
+ onlineMock.mockRestore()
+ })
+
+ it('should optimistically go to paused state if offline', async () => {
+ const onlineMock = mockNavigatorOnLine(false)
+ let count = 0
+ const states: Array = []
+
+ function Page() {
+ const mutation = useMutation(async (_text: string) => {
+ count++
+ await sleep(10)
+ return count
+ })
+
+ states.push(`${mutation.status}, ${mutation.isPaused}`)
+
+ return (
+
+
+
+ data: {mutation.data ?? 'null'}, status: {mutation.status},
+ isPaused: {String(mutation.isPaused)}
+
+
+ )
+ }
+
+ const rendered = renderWithClient(queryClient, )
+
+ await rendered.findByText('data: null, status: idle, isPaused: false')
+
+ rendered.getByRole('button', { name: /mutate/i }).click()
+
+ await rendered.findByText('data: null, status: loading, isPaused: true')
+
+ // no intermediate 'loading, false' state is expected because we don't start mutating!
+ expect(states[0]).toBe('idle, false')
+ expect(states[1]).toBe('loading, true')
+
+ onlineMock.mockReturnValue(true)
+ window.dispatchEvent(new Event('online'))
+
+ await rendered.findByText('data: 1, status: success, isPaused: false')
+
+ onlineMock.mockRestore()
+ })
+
it('should be able to retry a mutation when online', async () => {
const consoleMock = mockConsoleError()
const onlineMock = mockNavigatorOnLine(false)