Skip to content

Commit aec640a

Browse files
authored
feat(mutation): add onMutate to MutationCache (TanStack#3038)
1 parent dc2df10 commit aec640a

File tree

4 files changed

+41
-2
lines changed

4 files changed

+41
-2
lines changed

docs/src/pages/reference/MutationCache.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const mutationCache = new MutationCache({
1616
},
1717
onSuccess: data => {
1818
console.log(data)
19-
}
19+
},
2020
})
2121
```
2222

@@ -34,11 +34,16 @@ Its available methods are:
3434
- `onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => void`
3535
- Optional
3636
- This function will be called if some mutation is successful.
37+
- `onMutate?: (variables: unknown, mutation: Mutation) => void`
38+
- Optional
39+
- This function will be called before some mutation executes.
3740

3841
## Global callbacks
3942

40-
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:
43+
The `onError`, `onSuccess` and `onMutate` 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:
44+
4145
- `defaultOptions` can be overridden by each Mutation - the global callbacks will **always** be called.
46+
- `onMutate` does not allow returning a context value.
4247

4348
## `mutationCache.getAll`
4449

src/core/mutation.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ export class Mutation<
143143
if (!restored) {
144144
this.dispatch({ type: 'loading', variables: this.options.variables! })
145145
promise = promise
146+
.then(() => {
147+
// Notify cache callback
148+
this.mutationCache.config.onMutate?.(
149+
this.state.variables,
150+
this as Mutation<unknown, unknown, unknown, unknown>
151+
)
152+
})
146153
.then(() => this.options.onMutate?.(this.state.variables!))
147154
.then(context => {
148155
if (context !== this.state.context) {

src/core/mutationCache.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ interface MutationCacheConfig {
2020
context: unknown,
2121
mutation: Mutation<unknown, unknown, unknown, unknown>
2222
) => void
23+
onMutate?: (
24+
variables: unknown,
25+
mutation: Mutation<unknown, unknown, unknown, unknown>
26+
) => void
2327
}
2428

2529
type MutationCacheListener = (mutation?: Mutation) => void

src/core/tests/mutationCache.test.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,29 @@ describe('mutationCache', () => {
5353
)
5454
})
5555
})
56+
describe('MutationCacheConfig.onMutate', () => {
57+
test('should be called before a mutation executes', async () => {
58+
const consoleMock = mockConsoleError()
59+
const key = queryKey()
60+
const onMutate = jest.fn()
61+
const testCache = new MutationCache({ onMutate })
62+
const testClient = new QueryClient({ mutationCache: testCache })
63+
64+
try {
65+
await testClient.executeMutation({
66+
mutationKey: key,
67+
variables: 'vars',
68+
mutationFn: () => Promise.resolve({ data: 5 }),
69+
onMutate: () => 'context',
70+
})
71+
} catch {
72+
consoleMock.mockRestore()
73+
}
74+
75+
const mutation = testCache.getAll()[0]
76+
expect(onMutate).toHaveBeenCalledWith('vars', mutation)
77+
})
78+
})
5679

5780
describe('find', () => {
5881
test('should filter correctly', async () => {

0 commit comments

Comments
 (0)