import { fireEvent, waitFor } from '@testing-library/react'
import * as React from 'react'
import { useIsMutating } from '../useIsMutating'
import { useMutation } from '../useMutation'
import {
createQueryClient,
renderWithClient,
setActTimeout,
sleep,
} from './utils'
import { ErrorBoundary } from 'react-error-boundary'
import { QueryClient } from '@tanstack/query-core'
import * as MutationCacheModule from '../../../query-core/src/mutationCache'
describe('useIsMutating', () => {
it('should return the number of fetching mutations', async () => {
const isMutatings: number[] = []
const queryClient = createQueryClient()
function IsMutating() {
const isMutating = useIsMutating()
isMutatings.push(isMutating)
return null
}
function Mutations() {
const { mutate: mutate1 } = useMutation(['mutation1'], async () => {
await sleep(150)
return 'data'
})
const { mutate: mutate2 } = useMutation(['mutation2'], async () => {
await sleep(50)
return 'data'
})
React.useEffect(() => {
mutate1()
setActTimeout(() => {
mutate2()
}, 50)
}, [mutate1, mutate2])
return null
}
function Page() {
return (
)
}
renderWithClient(queryClient, )
await waitFor(() => expect(isMutatings).toEqual([0, 1, 2, 1, 0]))
})
it('should filter correctly by mutationKey', async () => {
const isMutatings: number[] = []
const queryClient = createQueryClient()
function IsMutating() {
const isMutating = useIsMutating(['mutation1'])
isMutatings.push(isMutating)
return null
}
function Page() {
const { mutate: mutate1 } = useMutation(['mutation1'], async () => {
await sleep(100)
return 'data'
})
const { mutate: mutate2 } = useMutation(['mutation2'], async () => {
await sleep(100)
return 'data'
})
React.useEffect(() => {
mutate1()
mutate2()
}, [mutate1, mutate2])
return
}
renderWithClient(queryClient, )
await waitFor(() => expect(isMutatings).toEqual([0, 1, 1, 0]))
})
it('should filter correctly by predicate', async () => {
const isMutatings: number[] = []
const queryClient = createQueryClient()
function IsMutating() {
const isMutating = useIsMutating({
predicate: (mutation) =>
mutation.options.mutationKey?.[0] === 'mutation1',
})
isMutatings.push(isMutating)
return null
}
function Page() {
const { mutate: mutate1 } = useMutation(['mutation1'], async () => {
await sleep(100)
return 'data'
})
const { mutate: mutate2 } = useMutation(['mutation2'], async () => {
await sleep(100)
return 'data'
})
React.useEffect(() => {
mutate1()
mutate2()
}, [mutate1, mutate2])
return
}
renderWithClient(queryClient, )
await waitFor(() => expect(isMutatings).toEqual([0, 1, 1, 0]))
})
it('should not change state if unmounted', async () => {
// We have to mock the MutationCache to not unsubscribe
// the listener when the component is unmounted
class MutationCacheMock extends MutationCacheModule.MutationCache {
subscribe(listener: any) {
super.subscribe(listener)
return () => void 0
}
}
const MutationCacheSpy = jest
.spyOn(MutationCacheModule, 'MutationCache')
.mockImplementation((fn) => {
return new MutationCacheMock(fn)
})
const queryClient = createQueryClient()
function IsMutating() {
useIsMutating()
return null
}
function Page() {
const [mounted, setMounted] = React.useState(true)
const { mutate: mutate1 } = useMutation(['mutation1'], async () => {
await sleep(10)
return 'data'
})
React.useEffect(() => {
mutate1()
}, [mutate1])
return (
{mounted && }
)
}
const { getByText } = renderWithClient(queryClient, )
fireEvent.click(getByText('unmount'))
// Should not display the console error
// "Warning: Can't perform a React state update on an unmounted component"
await sleep(20)
MutationCacheSpy.mockRestore()
})
describe('with custom context', () => {
it('should return the number of fetching mutations', async () => {
const context = React.createContext(undefined)
const isMutatings: number[] = []
const queryClient = new QueryClient()
function IsMutating() {
const isMutating = useIsMutating(undefined, { context })
isMutatings.push(isMutating)
return null
}
function Page() {
const { mutate: mutate1 } = useMutation(
['mutation1'],
async () => {
await sleep(150)
return 'data'
},
{ context },
)
const { mutate: mutate2 } = useMutation(
['mutation2'],
async () => {
await sleep(50)
return 'data'
},
{ context },
)
React.useEffect(() => {
mutate1()
setActTimeout(() => {
mutate2()
}, 50)
}, [mutate1, mutate2])
return
}
renderWithClient(queryClient, , { context })
await waitFor(() => expect(isMutatings).toEqual([0, 1, 1, 2, 2, 1, 0]))
})
it('should throw if the context is not passed to useIsMutating', async () => {
const context = React.createContext(undefined)
const isMutatings: number[] = []
const queryClient = new QueryClient()
function IsMutating() {
const isMutating = useIsMutating(undefined)
isMutatings.push(isMutating)
return null
}
function Page() {
const { mutate } = useMutation(['mutation'], async () => 'data', {
useErrorBoundary: true,
context,
})
React.useEffect(() => {
mutate()
}, [mutate])
return
}
const rendered = renderWithClient(
queryClient,
error boundary
}>
,
{ context },
)
await waitFor(() => rendered.getByText('error boundary'))
})
})
})