Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/src/pages/reference/MutationCache.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const mutationCache = new MutationCache({
onError: error => {
console.log(error)
},
onSuccess: data => {
console.log(data)
}
})
```

Expand All @@ -28,6 +31,14 @@ Its available methods are:
- `onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => void`
- Optional
- This function will be called if some mutation encounters an error.
- `onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => void`
- Optional
- This function will be called if some mutation is successful.

## Global callbacks

The `onError` and `onSuccess` callbacks on the MutationCache can be used to handle these events on a global level. They are different to `defaultOptions` provided to the QueryClient because:
- `defaultOptions` can be overridden by each Mutation - the global callbacks will **always** be called.

## `mutationCache.getAll`

Expand Down
14 changes: 13 additions & 1 deletion docs/src/pages/reference/QueryCache.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: QueryCache
title: QueryCache
---

The `QueryCache` is the storage mechanism for React Query. It stores all of the data, meta information and state of queries it contains.
The `QueryCache` is the storage mechanism for React Query. It stores all the data, meta information and state of queries it contains.

**Normally, you will not interact with the QueryCache directly and instead use the `QueryClient` for a specific cache.**

Expand All @@ -14,6 +14,9 @@ const queryCache = new QueryCache({
onError: error => {
console.log(error)
},
onSuccess: data => {
console.log(data)
}
})

const query = queryCache.find('posts')
Expand All @@ -31,6 +34,15 @@ Its available methods are:
- `onError?: (error: unknown, query: Query) => void`
- Optional
- This function will be called if some query encounters an error.
- `onSuccess?: (data: unknown, query: Query) => void`
- Optional
- This function will be called if some query is successful.

## Global callbacks

The `onError` and `onSuccess` callbacks on the QueryCache can be used to handle these events on a global level. They are different to `defaultOptions` provided to the QueryClient because:
- `defaultOptions` can be overridden by each Query - the global callbacks will **always** be called.
- `defaultOptions` callbacks will be called once for each Observer, while the global callbacks will only be called once per Query.

## `queryCache.find`

Expand Down
21 changes: 13 additions & 8 deletions src/core/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ export class Mutation<
.then(() => this.executeMutation())
.then(result => {
data = result
// Notify cache callback
this.mutationCache.config.onSuccess?.(
data,
this.state.variables,
this.state.context,
this as Mutation<unknown, unknown, unknown, unknown>
)
})
.then(() =>
this.options.onSuccess?.(
Expand All @@ -178,14 +185,12 @@ export class Mutation<
})
.catch(error => {
// Notify cache callback
if (this.mutationCache.config.onError) {
this.mutationCache.config.onError(
error,
this.state.variables,
this.state.context,
this as Mutation<unknown, unknown, unknown, unknown>
)
}
this.mutationCache.config.onError?.(
error,
this.state.variables,
this.state.context,
this as Mutation<unknown, unknown, unknown, unknown>
)

// Log error
getLogger().error(error)
Expand Down
6 changes: 6 additions & 0 deletions src/core/mutationCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ interface MutationCacheConfig {
context: unknown,
mutation: Mutation<unknown, unknown, unknown, unknown>
) => void
onSuccess?: (
data: unknown,
variables: unknown,
context: unknown,
mutation: Mutation<unknown, unknown, unknown, unknown>
) => void
}

type MutationCacheListener = (mutation?: Mutation) => void
Expand Down
7 changes: 4 additions & 3 deletions src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ export class Query<
onSuccess: data => {
this.setData(data as TData)

// Notify cache callback
this.cache.config.onSuccess?.(data, this as Query<any, any, any, any>)

// Remove query after fetching if cache time is 0
if (this.cacheTime === 0) {
this.optionalRemove()
Expand All @@ -442,9 +445,7 @@ export class Query<

if (!isCancelledError(error)) {
// Notify cache callback
if (this.cache.config.onError) {
this.cache.config.onError(error, this as Query<any, any, any, any>)
}
this.cache.config.onError?.(error, this as Query<any, any, any, any>)

// Log error
getLogger().error(error)
Expand Down
1 change: 1 addition & 0 deletions src/core/queryCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { QueryObserver } from './queryObserver'

interface QueryCacheConfig {
onError?: (error: unknown, query: Query<unknown, unknown, unknown>) => void
onSuccess?: (data: unknown, query: Query<unknown, unknown, unknown>) => void
}

interface QueryHashMap {
Expand Down
28 changes: 28 additions & 0 deletions src/core/tests/mutationCache.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,34 @@ describe('mutationCache', () => {
expect(onError).toHaveBeenCalledWith('error', 'vars', 'context', mutation)
})
})
describe('MutationCacheConfig.onSuccess', () => {
test('should be called when a mutation is successful', async () => {
const consoleMock = mockConsoleError()
const key = queryKey()
const onSuccess = jest.fn()
const testCache = new MutationCache({ onSuccess })
const testClient = new QueryClient({ mutationCache: testCache })

try {
await testClient.executeMutation({
mutationKey: key,
variables: 'vars',
mutationFn: () => Promise.resolve({ data: 5 }),
onMutate: () => 'context',
})
} catch {
consoleMock.mockRestore()
}

const mutation = testCache.getAll()[0]
expect(onSuccess).toHaveBeenCalledWith(
{ data: 5 },
'vars',
'context',
mutation
)
})
})

describe('find', () => {
test('should filter correctly', async () => {
Expand Down
14 changes: 14 additions & 0 deletions src/core/tests/queryCache.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,18 @@ describe('queryCache', () => {
expect(onError).toHaveBeenCalledWith('error', query)
})
})

describe('QueryCacheConfig.onSuccess', () => {
test('should be called when a query is successful', async () => {
const consoleMock = mockConsoleError()
const key = queryKey()
const onSuccess = jest.fn()
const testCache = new QueryCache({ onSuccess })
const testClient = new QueryClient({ queryCache: testCache })
await testClient.prefetchQuery(key, () => Promise.resolve({ data: 5 }))
consoleMock.mockRestore()
const query = testCache.find(key)
expect(onSuccess).toHaveBeenCalledWith({ data: 5 }, query)
})
})
})