-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
feat(query-core): add context to mutationfn & mutation callbacks #9615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughDocs, examples, core libraries, and framework bindings update mutation APIs to pass a MutationFunctionContext to mutationFn and lifecycle callbacks, add an explicit onMutateResult value returned from onMutate, replace many TContext type names with TOnMutateResult, and switch cache ops in examples to use context.client. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Caller
participant MO as MutationObserver
participant M as Mutation
participant MF as mutationFn
participant QC as QueryClient
Caller->>MO: mutate(variables)
MO->>M: execute(variables)
M->>M: build MutationFunctionContext { client, meta, mutationKey }
M->>MO: onMutate(variables, context) → onMutateResult
note right of MO: examples may call context.client.* for cache ops
M->>MF: mutationFn(variables, context)
MF-->>M: result / throw
alt success
M->>MO: onSuccess(data, variables, onMutateResult, context)
MO->>QC: context.client.setQueryData / invalidateQueries
else error
M->>MO: onError(error, variables, onMutateResult, context)
MO->>QC: rollback via onMutateResult using context.client
end
M->>MO: onSettled(data?, error?, variables, onMutateResult, context)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Pre-merge checks (3 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
docs/framework/react/guides/mutations.md (1)
189-214
: Fix usage: mutate is referenced but never captured.Destructure mutate from useMutation (or use mutation.mutate).
Apply:
-useMutation({ +const { mutate } = useMutation({ mutationFn: addTodo, - onSuccess: (data, variables, scope) => { + onSuccess: (data, variables, scope) => { // I will fire first }, ... }) mutate(todo, { onSuccess: (data, variables, scope) => { // I will fire second! }, ... })docs/framework/angular/guides/mutations.md (1)
249-251
: Bug in snippet: missingthis.
when calling mutate.Inside the class, reference the instance field.
Apply:
- mutation.mutate({ title: 'title' }) + this.mutation.mutate({ title: 'title' })docs/framework/angular/guides/optimistic-updates.md (1)
154-163
: Fix API usage: injectMutation expects a factory function.Examples should pass a function that returns options, not a plain object.
Apply:
-injectMutation({ +injectMutation(() => ({ mutationFn: updateTodo, // ... onSettled: (newTodo, error, variables, scope) => { if (error) { // do something } }, -}) +}))docs/framework/react/reference/useMutation.md (1)
145-147
: Typo: “query” → “mutation”.The “error” description refers to queries in a mutation hook doc.
- The error object for the query, if an error was encountered. + The error object for the mutation, if an error was encountered.packages/query-core/src/mutationObserver.ts (1)
151-154
: Drop non-null assertion when forwarding scope to onSuccess.Matches the proposed
scope: TScope | undefined
type and avoids passing an undefined value under a non-optional type.- if (action?.type === 'success') { - this.#mutateOptions.onSuccess?.(action.data, variables, context!) + if (action?.type === 'success') { + this.#mutateOptions.onSuccess?.(action.data, variables, context) this.#mutateOptions.onSettled?.(action.data, null, variables, context) } else if (action?.type === 'error') {
🧹 Nitpick comments (37)
packages/vue-query/src/useMutation.ts (1)
63-74
: Consider explicit generics for MutationObserver to strengthen inference.Being explicit avoids accidental widening if
options.value
loses type detail.- const observer = new MutationObserver(client, options.value) + const observer = new MutationObserver<TData, TError, TVariables, TScope>( + client, + options.value + )packages/solid-query/src/types.ts (1)
143-151
: Add a short doc blurb for TScopeA quick JSDoc on TScope will reduce confusion for users migrating from “context”.
Apply:
export interface SolidMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TScope = unknown, + /** + * Arbitrary "scope" value produced by onMutate and threaded to callbacks. + * Formerly referred to as "context". + */ + TScope = unknown, >docs/reference/MutationCache.md (2)
31-40
: Clarify where “scope” comes from in callback signaturesExplicitly state that “scope” is the value returned by the mutation’s onMutate (not the global one).
Apply:
- `onError?: (error: unknown, variables: unknown, scope: unknown, mutation: Mutation) => Promise<unknown> | unknown` + - `onError?: (error: unknown, variables: unknown, scope: unknown, mutation: Mutation) => Promise<unknown> | unknown` + - Note: `scope` is the value returned by the mutation’s own `onMutate` handler. ... - `onSuccess?: (data: unknown, variables: unknown, scope: unknown, mutation: Mutation) => Promise<unknown> | unknown` + - Note: `scope` is the value returned by the mutation’s own `onMutate` handler. ... - `onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, scope: unknown, mutation: Mutation) => Promise<unknown> | unknown` + - Note: `scope` is the value returned by the mutation’s own `onMutate` handler.
53-54
: Add a migration hint (context → scope) for readersA one-liner helps users upgrading existing code.
Apply:
- `onMutate` does not allow returning a scope value. + `onMutate` does not allow returning a scope value. + + Note: In prior versions, this value was referred to as “context” in docs and examples.packages/query-core/src/__tests__/mutationCache.test.tsx (1)
190-191
: Nice: global onMutate args assertedConsider an extra test proving that a return value from the global onMutate (even if provided) is ignored in favor of the mutation-level onMutate result—guards against regressions.
packages/query-core/src/__tests__/mutations.test.tsx (2)
635-636
: Consider adding a test that asserts mutationFn receives the new context.Nice note about “onMutate can return scope”. To fully exercise the feature, add a small test that asserts mutationFn’s 2nd arg exposes client/meta/mutationKey.
763-785
: Rename toscope
is good; drop stray console.log in the test.The
onSettled
signature update is correct. Remove the debug log to keep tests quiet.- console.log(results)
packages/query-core/src/utils.ts (1)
45-60
: Generic rename toTScope
inMutationFilters
is correct. Consider makingmatchMutation
generic too.Typing
matchMutation
generically improves inference forpredicate
.Additional change outside the selected lines (for
matchMutation
signature):-export function matchMutation( - filters: MutationFilters, - mutation: Mutation<any, any>, -): boolean { +export function matchMutation< + TData = unknown, + TError = DefaultError, + TVariables = unknown, + TScope = unknown, +>( + filters: MutationFilters<TData, TError, TVariables, TScope>, + mutation: Mutation<TData, TError, TVariables, TScope>, +): boolean {packages/react-query/src/useMutation.ts (1)
23-27
: Doc hint: clarify “scope” vs legacy “context”.Given MutationObserverResult may still surface a field named context to avoid a breaking change, consider a short JSDoc note on TScope explaining the transitional naming to preempt confusion.
docs/framework/svelte/reference/type-aliases/createmutationresult.md (1)
6-6
: Cross-link MutationFunctionContext for discoverability.Add a “See also” link to the new MutationFunctionContext so readers find the new context passed to mutationFn.
docs/framework/svelte/reference/type-aliases/createmutatefunction.md (1)
6-10
: Show args type inline for clarity.Make the code block explicit about the args type to avoid readers missing the Parameters section.
-```ts -type CreateMutateFunction<TData, TError, TVariables, TScope> = (...args) => void -``` +```ts +type CreateMutateFunction<TData, TError, TVariables, TScope> = + (...args: Parameters<MutateFunction<TData, TError, TVariables, TScope>>) => void +```docs/framework/svelte/reference/type-aliases/createmutationoptions.md (1)
25-25
: Add brief “scope” usage note.A one-liner describing TScope as the value returned from onMutate and received by success/error/settled callbacks would help readers connect concepts.
docs/framework/angular/reference/functions/mutationoptions.md (1)
9-12
: Consider adding a short TScope example.Add a snippet showing onMutate returning a scope and onSuccess receiving it to make the TScope flow tangible in Angular.
docs/framework/svelte/reference/type-aliases/createbasemutationresult.md (1)
9-13
: Clarify TScope vs. MutationObserverResult type-param naming to avoid confusionMutationObserverResult in v5 still documents its 4th generic as TContext. Using TScope here is fine (it’s just a type argument), but readers may be confused when cross-referencing. Consider adding a short note that “TScope maps 1:1 to the MutationObserverResult fourth generic (named TContext in v5 docs).”
Proposed doc tweak:
type CreateBaseMutationResult<TData, TError, TVariables, TScope> = Override< - MutationObserverResult<TData, TError, TVariables, TScope>, + MutationObserverResult<TData, TError, TVariables, TScope>, { mutate: CreateMutateFunction<TData, TError, TVariables, TScope> } > & object + +> Note: In v5, MutationObserverResult’s 4th type parameter is named `TContext`. Here, `TScope` maps 1:1 to that parameter.packages/svelte-query/src/createMutation.ts (1)
19-41
: Tie options subscription to store lifecycle and simplify mutate initializationAvoid a dangling options subscription and redundant late init of mutate. Subscribe/unsubscribe within the readable’s start/stop, and initialize mutate once as a stable closure.
Apply:
const observer = new MutationObserver<TData, TError, TVariables, TScope>( client, get(optionsStore), ) - let mutate: CreateMutateFunction<TData, TError, TVariables, TScope> - - optionsStore.subscribe(($options) => { - mutate = (variables, mutateOptions) => { - observer.mutate(variables, mutateOptions).catch(noop) - } - observer.setOptions($options) - }) + const mutate: CreateMutateFunction<TData, TError, TVariables, TScope> = ( + variables, + mutateOptions, + ) => { + observer.mutate(variables, mutateOptions).catch(noop) + } - const result = readable(observer.getCurrentResult(), (set) => { - return observer.subscribe(notifyManager.batchCalls((val) => set(val))) - }) + const result = readable(observer.getCurrentResult(), (set) => { + const unsubscribeObserver = observer.subscribe( + notifyManager.batchCalls((val) => set(val)), + ) + const unsubscribeOptions = optionsStore.subscribe(($options) => { + observer.setOptions($options) + }) + return () => { + unsubscribeOptions() + unsubscribeObserver() + } + })docs/framework/angular/reference/interfaces/createmutationoptions.md (1)
6-6
: Add a brief note about the TContext → TScope rename for Angular docsFor users upgrading or cross-reading older pages, explicitly noting the rename reduces friction.
Proposed addition:
# Interface: CreateMutationOptions<TData, TError, TVariables, TScope> + +> Note: In v5 docs, some references still use `TContext` as the 4th generic. In this page, `TScope` is used and maps 1:1 to that parameter.Also applies to: 10-10, 20-20
docs/framework/angular/reference/type-aliases/createmutationresult.md (1)
6-11
: CreateMutationResult: TScope threading reads correctly.BaseMutationNarrowing and TState now use TScope consistently.
TVariables defaults to unknown here but void in other Angular docs (e.g., injectMutation). Consider aligning defaults for consistency.
Ensure BaseMutationNarrowing’s generic order is TData, TError, TVariables, TScope in the referenced page.
Also applies to: 22-25
examples/react/optimistic-updates-cache/src/pages/index.tsx (3)
56-58
: Type previousTodos to avoid implicit any/unknown.Apply:
- const previousTodos = context.client.getQueryData( - todoListOptions.queryKey, - ) + const previousTodos = context.client.getQueryData<Todos>( + todoListOptions.queryKey, + )
49-54
: Strengthen scope typing and use queryOptions for invalidation.Add an explicit Scope type and annotate onMutate’s return, onError/onSettled params; use todoListOptions for symmetry.
Apply:
- onMutate: async (newTodo: string, context: MutationFunctionContext) => { + onMutate: async (newTodo: string, context: MutationFunctionContext): Promise<Scope> => { @@ - return { previousTodos, client: context.client } + return { previousTodos, client: context.client } }, @@ - onError: (err, variables, scope) => { + onError: (err, variables, scope: Scope) => { if (scope?.previousTodos) { scope.client.setQueryData<Todos>(['todos'], scope.previousTodos) } }, @@ - onSettled: (data, error, variables, scope) => - scope?.client.invalidateQueries({ queryKey: ['todos'] }), + onSettled: (data, error, variables, scope: Scope) => + scope?.client.invalidateQueries(todoListOptions),And add near the top (outside changed lines):
type Scope = { previousTodos?: Todos; client: QueryClient }Also applies to: 71-83
49-54
: Remove unused useQueryClient hook and variable.Since you now use context.client, drop useQueryClient and the queryClient variable to satisfy lint rules.
Outside changed lines, update:
// Remove from import list: // useQueryClient, // Remove: // const queryClient = useQueryClient()packages/angular-query-experimental/src/inject-mutation.ts (1)
81-88
: Minor: stable mutate wrapper identity (optional).The arrow wrapper for mutate is recreated when the computed re-evaluates. If consumers rely on referential equality, consider memoizing the wrapper per observer instance. Not a blocker.
docs/framework/react/guides/mutations.md (3)
146-161
: Clarify “context” vs “scope” roles in callbacks.Consider a short callout: context (second arg to onMutate) is the system-provided MutationFunctionContext; scope is the object you return from onMutate and is passed to onSuccess/onError/onSettled. This avoids confusion with the “scope” option shown later.
297-309
: Make optimistic update helpers resilient to undefined cache state.old can be undefined. Use nullish coalescing to avoid runtime errors in examples.
Apply:
- context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + context.client.setQueryData(['todos'], (old) => [...(old ?? []), optimisticTodo]) ... - scope.client.setQueryData(['todos'], (old) => - old.map((todo) => (todo.id === scope.optimisticTodo.id ? result : todo)), - ) + scope.client.setQueryData(['todos'], (old = []) => + old.map((todo) => (todo.id === scope.optimisticTodo.id ? result : todo)), + ) ... - scope.client.setQueryData(['todos'], (old) => - old.filter((todo) => todo.id !== scope.optimisticTodo.id), - ) + scope.client.setQueryData(['todos'], (old = []) => + old.filter((todo) => todo.id !== scope.optimisticTodo.id), + )Also applies to: 311-321
391-402
: Terminology collision: “scope” option vs “scope” callback arg.Since both are named “scope” but serve different purposes (queueing vs payload), add a one-liner note to distinguish them to readers.
docs/framework/angular/guides/mutations.md (2)
93-110
: Angular docs: add a brief note distinguishing context vs scope.Same suggestion as React docs to reduce ambiguity for readers.
214-241
: Harden optimistic update examples against undefined cache state.Align with resilient patterns so examples won’t throw if the todos query hasn’t run yet.
Apply:
- await context.client.cancelQueries({ queryKey: ['todos'] }) + await context.client.cancelQueries({ queryKey: ['todos'] }) ... - context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + context.client.setQueryData(['todos'], (old) => [...(old ?? []), optimisticTodo]) ... - scope.client.setQueryData(['todos'], (old) => - old.map((todo) => (todo.id === scope.optimisticTodo.id ? result : todo)), - ) + scope.client.setQueryData(['todos'], (old = []) => + old.map((todo) => (todo.id === scope.optimisticTodo.id ? result : todo)), + ) ... - scope.client.setQueryData(['todos'], (old) => - old.filter((todo) => todo.id !== scope.optimisticTodo.id), - ) + scope.client.setQueryData(['todos'], (old = []) => + old.filter((todo) => todo.id !== scope.optimisticTodo.id), + )docs/framework/angular/guides/optimistic-updates.md (1)
90-103
: Make setQueryData calls safe when cache is empty.Use nullish coalescing/defaults to avoid spreading/iterating undefined.
Apply:
- await context.client.cancelQueries({ queryKey: ['todos'] }) + await context.client.cancelQueries({ queryKey: ['todos'] }) - const previousTodos = context.client.getQueryData(['todos']) + const previousTodos = context.client.getQueryData(['todos']) - context.client.setQueryData(['todos'], (old) => [...old, newTodo]) + context.client.setQueryData(['todos'], (old) => [...(old ?? []), newTodo]) ... - scope.client.invalidateQueries({ queryKey: ['todos'] }) + scope.client.invalidateQueries({ queryKey: ['todos'] })Also applies to: 110-113, 125-138, 140-147
packages/angular-query-experimental/src/mutation-options.ts (1)
52-59
: Overload 2 matches the first; minor style nit on Omit vs OmitKeyof.Elsewhere in angular-query-experimental you use OmitKeyof; consider using it here for consistency, but it’s not functionally required.
- options: Omit< - CreateMutationOptions<TData, TError, TVariables, TScope>, - 'mutationKey' - >, -): Omit<CreateMutationOptions<TData, TError, TVariables, TScope>, 'mutationKey'> + options: OmitKeyof< + CreateMutationOptions<TData, TError, TVariables, TScope>, + 'mutationKey' + >, +): OmitKeyof<CreateMutationOptions<TData, TError, TVariables, TScope>, 'mutationKey'>docs/framework/react/guides/optimistic-updates.md (2)
111-112
: Handle undefined cache data in setQueryData updater.old can be undefined; spreading will throw. Use a null-safe pattern.
- context.client.setQueryData(['todos'], (old) => [...old, newTodo]) + context.client.setQueryData(['todos'], (old) => [ ...(old ?? []), newTodo ])
100-101
: Use a more accurate function name in “adding a new todo”.The example says “adding a new todo” but uses updateTodo. Rename to addTodo for clarity.
-useMutation({ - mutationFn: updateTodo, +useMutation({ + mutationFn: addTodo,docs/framework/react/reference/useMutation.md (2)
93-97
: Clarify default scope semantics.Code treats “unscoped” mutations as fully concurrent; it doesn’t generate a default unique scope id. Reword to avoid implying an auto-id.
- Optional - Defaults to a unique id (so that all mutations run in parallel) - Mutations with the same scope id will run in serial + Optional + Unscoped by default (mutations run in parallel). + Mutations with the same `scope.id` will run in serial.
51-56
: Minor wording nits.Tighten phrasing per the new context param and grammar.
- A function that performs an asynchronous task and returns a promise. - `variables` is an object that `mutate` will pass to your `mutationFn` - `context` is an object that `mutate` will pass to your `mutationFn`. Contains reference to `QueryClient`, `mutationKey` and optional `meta` object. + A function that performs an asynchronous task and returns a promise. + `variables` is the value that `mutate` passes to your `mutationFn`. + `context` is passed to your `mutationFn` and contains `{ client: QueryClient, mutationKey?, meta? }`.packages/query-core/src/mutationCache.ts (2)
195-208
: Type the filters generically to drop casts.Widen
find
’sfilters
toMutationFilters<TData, TError, TVariables, TScope>
so the return cast isn’t needed.- find< + find< TData = unknown, TError = DefaultError, TVariables = any, - TScope = unknown, - >( - filters: MutationFilters, - ): Mutation<TData, TError, TVariables, TScope> | undefined { + TScope = unknown, + >(filters: MutationFilters<TData, TError, TVariables, TScope>): + Mutation<TData, TError, TVariables, TScope> | undefined { const defaultedFilters = { exact: true, ...filters } return this.getAll().find((mutation) => matchMutation(defaultedFilters, mutation), - ) as Mutation<TData, TError, TVariables, TScope> | undefined + ) as Mutation<TData, TError, TVariables, TScope> | undefined }
146-149
: Consider notifying only on actual removals.You currently notify “removed” even when nothing was deleted. If listeners rely on semantics, this may be confusing.
packages/query-core/src/mutation.ts (1)
231-236
: Optional: expose MutationFunctionContext to cache-level callbacks.If useful for plugins/integrations, consider adding the context object to
MutationCacheConfig
handlers for symmetry. Non-breaking since it would be an extra param.Also applies to: 241-247, 256-261, 270-276
packages/query-core/src/types.ts (1)
1100-1103
: Consider making the new context arg optional to reduce friction.This keeps existing user-annotated
MutationFunction
types (single-arg) source-compatible without relying on bivariance settings.-export type MutationFunction<TData = unknown, TVariables = unknown> = ( +export type MutationFunction<TData = unknown, TVariables = unknown> = ( variables: TVariables, - context: MutationFunctionContext, + context?: MutationFunctionContext, ) => Promise<TData>If you prefer keeping it required, please confirm repo-wide TS compatibility under
strictFunctionTypes: true
.packages/query-core/src/mutationObserver.ts (1)
147-147
: Optional: invoke mutateOptions callbacks even without listeners.Per-call callbacks shouldn’t depend on subscription presence. Consider removing
hasListeners()
from the guard.- if (this.#mutateOptions && this.hasListeners()) { + if (this.#mutateOptions) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (45)
docs/framework/angular/guides/mutations.md
(4 hunks)docs/framework/angular/guides/optimistic-updates.md
(3 hunks)docs/framework/angular/reference/functions/injectmutation.md
(3 hunks)docs/framework/angular/reference/functions/mutationoptions.md
(2 hunks)docs/framework/angular/reference/interfaces/basemutationnarrowing.md
(6 hunks)docs/framework/angular/reference/interfaces/createmutationoptions.md
(2 hunks)docs/framework/angular/reference/type-aliases/createbasemutationresult.md
(3 hunks)docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutatefunction.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutationresult.md
(2 hunks)docs/framework/react/guides/mutations.md
(4 hunks)docs/framework/react/guides/optimistic-updates.md
(3 hunks)docs/framework/react/reference/useMutation.md
(3 hunks)docs/framework/svelte/reference/functions/createmutation.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createbasemutationresult.md
(3 hunks)docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutatefunction.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutationoptions.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutationresult.md
(2 hunks)docs/reference/MutationCache.md
(2 hunks)examples/react/optimistic-updates-cache/src/pages/index.tsx
(3 hunks)packages/angular-query-experimental/src/inject-mutation.ts
(5 hunks)packages/angular-query-experimental/src/mutation-options.ts
(2 hunks)packages/angular-query-experimental/src/types.ts
(7 hunks)packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts
(2 hunks)packages/query-core/src/__tests__/mutationCache.test.tsx
(7 hunks)packages/query-core/src/__tests__/mutations.test.tsx
(4 hunks)packages/query-core/src/__tests__/utils.test.tsx
(1 hunks)packages/query-core/src/mutation.ts
(9 hunks)packages/query-core/src/mutationCache.ts
(4 hunks)packages/query-core/src/mutationObserver.ts
(8 hunks)packages/query-core/src/queryClient.ts
(1 hunks)packages/query-core/src/types.ts
(8 hunks)packages/query-core/src/utils.ts
(2 hunks)packages/react-query/src/__tests__/mutationOptions.test-d.tsx
(1 hunks)packages/react-query/src/mutationOptions.ts
(1 hunks)packages/react-query/src/types.ts
(1 hunks)packages/react-query/src/useMutation.ts
(2 hunks)packages/solid-query/src/types.ts
(1 hunks)packages/solid-query/src/useMutation.ts
(1 hunks)packages/svelte-query/src/createMutation.ts
(1 hunks)packages/svelte-query/src/types.ts
(1 hunks)packages/vue-query/src/mutationCache.ts
(1 hunks)packages/vue-query/src/queryClient.ts
(1 hunks)packages/vue-query/src/useMutation.ts
(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (22)
packages/vue-query/src/queryClient.ts (1)
packages/query-core/src/types.ts (1)
MutationObserverOptions
(1142-1149)
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (3)
packages/angular-query-experimental/src/mutation-options.ts (1)
mutationOptions
(90-99)packages/react-query/src/mutationOptions.ts (1)
mutationOptions
(29-38)packages/query-core/src/types.ts (1)
DefaultError
(47-51)
packages/query-core/src/queryClient.ts (1)
packages/query-core/src/types.ts (2)
OmitKeyof
(19-29)MutationObserverOptions
(1142-1149)
packages/vue-query/src/mutationCache.ts (3)
packages/vue-query/src/types.ts (1)
MaybeRefDeep
(29-37)packages/query-core/src/utils.ts (1)
MutationFilters
(45-67)packages/query-core/src/mutation.ts (1)
Mutation
(84-362)
packages/angular-query-experimental/src/inject-mutation.ts (2)
packages/angular-query-experimental/src/types.ts (3)
CreateMutationOptions
(162-170)CreateMutationResult
(301-314)CreateMutateFunction
(175-182)packages/query-core/src/mutationObserver.ts (1)
MutationObserver
(22-171)
packages/query-core/src/mutationObserver.ts (4)
packages/query-core/src/types.ts (3)
MutationObserverResult
(1310-1319)MutationObserverOptions
(1142-1149)MutateOptions
(1151-1169)packages/query-core/src/subscribable.ts (1)
Subscribable
(1-30)packages/query-core/src/queryClient.ts (1)
QueryClient
(61-648)packages/query-core/src/mutation.ts (4)
Mutation
(84-362)action
(293-361)Action
(74-80)getDefaultState
(364-381)
packages/react-query/src/mutationOptions.ts (2)
packages/react-query/src/types.ts (1)
UseMutationOptions
(192-200)packages/angular-query-experimental/src/mutation-options.ts (1)
mutationOptions
(90-99)
packages/angular-query-experimental/src/types.ts (2)
packages/query-core/src/types.ts (5)
OmitKeyof
(19-29)MutationObserverOptions
(1142-1149)MutateFunction
(1171-1179)Override
(31-35)MutationObserverResult
(1310-1319)packages/svelte-query/src/types.ts (4)
CreateMutateFunction
(96-103)CreateMutateAsyncFunction
(105-110)CreateBaseMutationResult
(112-122)CreateMutationResult
(125-130)
packages/react-query/src/types.ts (2)
packages/query-core/src/types.ts (6)
OmitKeyof
(19-29)MutationObserverOptions
(1142-1149)DefaultError
(47-51)MutateFunction
(1171-1179)Override
(31-35)MutationObserverResult
(1310-1319)packages/solid-query/src/types.ts (4)
UseMutateFunction
(160-167)UseMutateAsyncFunction
(169-174)UseBaseMutationResult
(176-186)UseMutationResult
(188-193)
packages/svelte-query/src/createMutation.ts (4)
packages/svelte-query/src/types.ts (4)
StoreOrVal
(21-21)CreateMutationOptions
(86-94)CreateMutationResult
(125-130)CreateMutateFunction
(96-103)packages/svelte-query/src/useQueryClient.ts (1)
useQueryClient
(4-7)packages/svelte-query/src/utils.ts (1)
isSvelteStore
(4-8)packages/query-core/src/mutationObserver.ts (2)
MutationObserver
(22-171)mutate
(111-126)
packages/query-core/src/__tests__/mutations.test.tsx (1)
packages/query-codemods/src/v5/remove-overloads/utils/index.cjs (1)
scope
(29-31)
packages/solid-query/src/types.ts (1)
packages/query-core/src/types.ts (5)
MutationObserverOptions
(1142-1149)DefaultError
(47-51)MutateFunction
(1171-1179)Override
(31-35)MutationObserverResult
(1310-1319)
packages/react-query/src/useMutation.ts (2)
packages/react-query/src/types.ts (3)
UseMutationOptions
(192-200)UseMutationResult
(228-233)UseMutateFunction
(202-209)packages/query-core/src/mutationObserver.ts (1)
MutationObserver
(22-171)
packages/angular-query-experimental/src/mutation-options.ts (4)
packages/query-core/src/types.ts (2)
WithRequired
(443-445)DefaultError
(47-51)packages/angular-query-experimental/src/types.ts (1)
CreateMutationOptions
(162-170)packages/svelte-query/src/types.ts (1)
CreateMutationOptions
(86-94)packages/react-query/src/mutationOptions.ts (1)
mutationOptions
(29-38)
packages/svelte-query/src/types.ts (2)
packages/query-core/src/types.ts (6)
OmitKeyof
(19-29)MutationObserverOptions
(1142-1149)DefaultError
(47-51)MutateFunction
(1171-1179)Override
(31-35)MutationObserverResult
(1310-1319)packages/angular-query-experimental/src/types.ts (3)
CreateMutateFunction
(175-182)CreateMutateAsyncFunction
(187-192)CreateBaseMutationResult
(197-207)
packages/vue-query/src/useMutation.ts (3)
packages/query-core/src/types.ts (6)
DistributiveOmit
(14-17)MutationObserverResult
(1310-1319)MutationObserverOptions
(1142-1149)DefaultError
(47-51)MutateFunction
(1171-1179)MutateOptions
(1151-1169)packages/vue-query/src/types.ts (2)
ShallowOption
(58-63)MaybeRefDeep
(29-37)packages/react-query/src/useMutation.ts (1)
useMutation
(19-66)
packages/query-core/src/types.ts (3)
packages/query-core/src/queryClient.ts (1)
QueryClient
(61-648)packages/vue-query/src/queryClient.ts (1)
QueryClient
(37-477)packages/query-core/src/mutation.ts (1)
MutationState
(26-41)
packages/solid-query/src/useMutation.ts (2)
packages/solid-query/src/types.ts (3)
UseMutationOptions
(153-158)UseMutationResult
(188-193)UseMutateFunction
(160-167)packages/query-core/src/mutationObserver.ts (3)
MutationObserver
(22-171)mutate
(111-126)state
(128-142)
packages/query-core/src/mutationCache.ts (3)
packages/query-core/src/mutation.ts (2)
Mutation
(84-362)MutationState
(26-41)packages/query-core/src/types.ts (1)
MutationOptions
(1105-1140)packages/query-core/src/utils.ts (2)
MutationFilters
(45-67)matchMutation
(172-199)
examples/react/optimistic-updates-cache/src/pages/index.tsx (2)
packages/query-core/src/types.ts (1)
MutationFunctionContext
(1094-1098)examples/svelte/playground/src/lib/stores.ts (1)
Todos
(25-25)
packages/query-core/src/utils.ts (2)
packages/query-core/src/mutation.ts (1)
Mutation
(84-362)packages/query-core/src/index.ts (1)
Mutation
(44-44)
packages/query-core/src/mutation.ts (2)
packages/query-core/src/mutationCache.ts (1)
MutationCache
(84-231)packages/query-core/src/types.ts (3)
MutationOptions
(1105-1140)DefaultError
(47-51)MutationFunctionContext
(1094-1098)
🪛 LanguageTool
docs/framework/react/reference/useMutation.md
[grammar] ~53-~53: There might be a mistake here.
Context: ...asynchronous task and returns a promise. - variables
is an object that mutate
will pass to...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...pe | void> | TScope | void` - Optional - This function will fire before the mutat...
(QB_NEW_EN)
🔇 Additional comments (66)
packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts (2)
146-149
: Rename looks consistent with scope migration.The updated callback params and log text align with the new “scope” naming. Tests should keep exercising only ordering, not signature specifics, so this is fine.
205-210
: Regression sample updated correctly.Strings and parameter names reflect the new “scope” term and should keep the rule behavior intact.
packages/vue-query/src/mutationCache.ts (1)
11-19
: Drop generic parameterization in the Vue wrapper —super.find
is declared with a non-genericfilters: MutationFilters
(defaults to<unknown, DefaultError, unknown, unknown>
), so changing the wrapper to acceptMutationFilters<TData, TError, TVariables, TScope>
will not satisfy the base signature. To preserve strong typing, the coreMutationCache.find
signature itself must be updated to tie itsfilters
parameter to its own generics.Likely an incorrect or invalid review comment.
packages/vue-query/src/queryClient.ts (1)
455-465
: Generic rename to TScope looks good and matches core types.This is a type-only rename; runtime behavior is unchanged. No further action.
packages/vue-query/src/useMutation.ts (5)
27-41
: Public generics migrated to TScope — OK.Aliases and options now consistently use
TScope
. This aligns with core’sMutationObserverOptions
andMutateOptions
.
51-61
: Return type updated to carry TScope through — OK.
mutate
,mutateAsync
, andreset
signatures match core generics.
27-34
: Check PR description vs. code regarding MutationObserverResult generic name.Code uses
MutationObserverResult<..., TScope>
, but the PR summary says it remains oncontext
to avoid breaking changes. Please confirm which is intended and update the PR description or types accordingly.
95-99
: MutateOptions now parameterized by TScope — OK.Call-through to
observer.mutate
remains correct with the new typing.
116-118
: Casting result refs with TScope — OK.Keeps the reactive surface aligned with the new generic.
packages/solid-query/src/types.ts (1)
147-151
: Approve code changes – TScope rename fully propagated; no lingering TContext references. Generics align with query-core’s MutationObserverOptions/Result and MutateFunction; no runtime impact.packages/solid-query/src/useMutation.ts (1)
19-24
: Solid useMutation generic rename is consistentTScope is threaded through options, observer, and returned state correctly. No functional changes; mutate vs mutateAsync separation preserved.
Also applies to: 26-33, 39-40
packages/query-core/src/__tests__/mutationCache.test.tsx (2)
30-43
: Tests correctly assert scope threading on error pathReturning 'scope' from onMutate and asserting it arrives at global onError/onSettled matches the intended contract.
Also applies to: 47-53
112-125
: Tests correctly assert scope threading on success pathThe success flow mirrors the error case; expectations look right.
Also applies to: 129-135
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (1)
57-66
: Type inference for scope looks goodThe dts test now validates scope inference; matches the updated generics.
packages/query-core/src/__tests__/utils.test.tsx (1)
426-430
: Verified allnew Mutation
calls includeclient
. The only direct invocations inutils.test.tsx
andmutationCache.ts
both pass theclient
argument.packages/query-core/src/__tests__/mutations.test.tsx (1)
815-817
:onSettled
error-path usesscope
correctly.Expectations read well and match the renamed parameter.
packages/query-core/src/queryClient.ts (1)
511-522
: Type rename TContext → TScope insetMutationDefaults
is consistent.No runtime impact; matches the broader generic rename.
docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md (2)
6-11
: Docs updated to TScope — looks good.Alias now maps to
MutateFunction<..., TScope>
consistently.
21-21
: No lingeringTContext
references found indocs/framework/svelte
; resolving sweep.packages/react-query/src/useMutation.ts (2)
23-27
: Approve TScope rename. The generic parameter rename is consistent, non-breaking, and no strayTContext
usages remain in packages/react-query/src.
49-51
: TScope forwarding and callback signatures verified. onMutate, onSuccess, onError, and onSettled all use the generic TScope (TContext) and the MutationFunctionContext is wired end-to-end.docs/framework/svelte/reference/type-aliases/createmutationresult.md (2)
6-6
: TScope rename is correct and aligned with core.Header and alias now use TScope; matches the repo-wide shift. LGTM.
Also applies to: 9-10
18-25
: Confirm TVariables default for CreateBaseMutationResult
Ensure theCreateBaseMutationResult
(orCreateMutationResult
) type inpackages/svelte-query
defines itsTVariables
default asunknown
, matching the docs’ example.docs/framework/svelte/reference/type-aliases/createmutatefunction.md (1)
20-27
: TScope rename and parameter docs look good.docs/framework/svelte/reference/type-aliases/createmutationoptions.md (1)
6-6
: TScope rename matches underlying MutationObserverOptions.Signature aligns with core. Good.
Also applies to: 9-10
docs/framework/angular/reference/functions/mutationoptions.md (2)
9-12
: Angular docs correctly switched to TScope.Function generics, param, and return types all updated. Looks consistent.
Also applies to: 49-49, 55-55, 61-61
9-12
: Sanity-check TError default = Error across Angular package.rg search in packages/angular-query-experimental returned no matches — verify whether mutationOptions in the Angular codebase intentionally defaults TError = Error or should align with other frameworks' DefaultError; update types/docs if inconsistent.
docs/framework/angular/reference/functions/injectmutation.md (1)
9-13
: TScope rename in injectMutation docs — LGTM; verify cross-links.The rename from TContext to TScope reads consistently across signature, params, and returns.
Please double-check:
- Linked pages CreateMutationOptions and CreateMutationResult now document TScope consistently.
- InjectMutationOptions references TScope too.
- TVariables default (void here) matches other Angular mutation docs to avoid confusion.
Also applies to: 27-27, 33-33, 45-45
docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md (1)
6-6
: CreateMutateAsyncFunction: TScope rename looks good.Type alias and params align with MutateFunction’s TScope.
Confirm MutateFunction<TData, TError, TVariables, TScope> is updated wherever it’s documented/re-exported in Angular docs.
Also applies to: 9-11, 21-21
packages/react-query/src/mutationOptions.ts (1)
8-16
: Approve and merge
Confirmed no remainingTContext
references in packages/react-query/src—mutationOptions overloads updated toTScope
correctly.examples/react/optimistic-updates-cache/src/pages/index.tsx (1)
12-12
: Import path for MutationFunctionContext is correct
MutationFunctionContext is re-exported by @tanstack/react-query via theexport * from '@tanstack/query-core'
inpackages/react-query/src/index.ts
, so the import can remain unchanged.packages/angular-query-experimental/src/inject-mutation.ts (3)
49-56
: TScope rename is correctly threaded through the public API.Generic parameter rename from TContext → TScope is applied consistently in the function signature and options factory. No behavioral changes.
73-83
: Observer/result/mutate types consistently use TScope.Types for MutationObserver, CreateMutateFunction, and MutationObserverResult (in the subscriber signal and final cast) are aligned. Good cohesion with query-core generics.
Also applies to: 101-106, 166-171
59-64
: No remainingTContext
in Angular sources
Search across the repo shows noTContext
references in the Angular packages—only in documentation and React guides.docs/framework/angular/reference/type-aliases/createbasemutationresult.md (1)
6-16
: Docs: TScope rename looks accurate.Type alias, members, and Type Parameters now use TScope consistently. Link targets remain valid.
Also applies to: 22-24, 34-34
packages/angular-query-experimental/src/mutation-options.ts (3)
38-46
: TScope propagation in overload 1 looks correct.Generic rename is consistent with CreateMutationOptions<TData, TError, TVariables, TScope> and WithRequired<'mutationKey'>. No functional change.
90-99
: Implementation signature aligns with overloads.Identity return preserves generics with TScope. Looks good.
34-47
: No TContext occurrences detected
Confirmed viarg -nP '\bTContext\b' packages/angular-query-experimental
returning zero matches.docs/framework/react/guides/optimistic-updates.md (3)
137-150
: Same concerns for the single-todo example.Verify onMutate’s context param support; otherwise mirror the fallback shown above using useQueryClient and pass client via scope.
- onMutate: async (newTodo, context) => { - await context.client.cancelQueries({ queryKey: ['todos', newTodo.id] }) - const previousTodo = context.client.getQueryData(['todos', newTodo.id]) - context.client.setQueryData(['todos', newTodo.id], newTodo) - return { previousTodo, newTodo, client: context.client } - }, + onMutate: async (newTodo) => { + await client.cancelQueries({ queryKey: ['todos', newTodo.id] }) + const previousTodo = client.getQueryData(['todos', newTodo.id]) + client.setQueryData(['todos', newTodo.id], newTodo) + return { previousTodo, newTodo, client } + },
122-124
: Scope usage across callbacks is consistent.onError/onSettled signatures correctly accept scope as the last parameter and use scope.client.
Also applies to: 156-158, 171-176
102-115
: Drop this check:onMutate
does receive thecontext
parameter. TheMutationOptions
type inpackages/query-core/src/types.ts
definesonMutate?(variables, context: MutationFunctionContext)
(lines 1113–1115), so the docs example is correct.Likely an incorrect or invalid review comment.
packages/react-query/src/types.ts (6)
191-201
: Rename to TScope in UseMutationOptions is correct and non-breaking.Type positionally remains 4th generic; mapping to MutationObserverOptions<TData, TError, TVariables, TScope> is consistent with core.
202-210
: UseMutateFunction forwards TScope to MutateFunction.Correctly narrows mutate’s call signature while keeping fire-and-forget return type.
211-217
: UseMutateAsyncFunction alias updated to TScope.Matches MutateFunction from core.
218-227
: UseBaseMutationResult now parameterized with TScope.Override of MutationObserverResult and mutate/mutateAsync members stay in sync.
228-234
: UseMutationResult alias follows TScope.All downstream generics now consistently use TScope.
191-201
: Public types no longer include TContext Verified via ripgrep: no TContext references in the codebase (only in docs).packages/svelte-query/src/types.ts (2)
90-95
: TScope propagation across createMutation types looks consistent.CreateMutationOptions, mutate function types, base/result types all forward TScope into core generics.
Also applies to: 96-104, 105-111, 112-122, 124-131
85-95
: No remaining TContext references. A ripgrep search (rg -nP '\bTContext\b' packages/svelte-query
) returned zero matches, confirming all TContext types have been removed.packages/angular-query-experimental/src/types.ts (3)
166-170
: TScope rename across Angular mutation option/result types is coherent.All relevant aliases (CreateMutationOptions, CreateMutate(Function|Async)Function, CreateBaseMutationResult) forward TScope to core generics.
Also applies to: 175-183, 187-193, 197-207
236-249
: Type guards updated to TScope are accurate.this-narrowing for isSuccess/isError/isPending/isIdle now references CreateMutationResult<..., TScope> and status-based extracts correctly.
Also applies to: 253-266, 270-283, 287-295
212-221
: TContext references fully removed across frameworks
No instances ofTContext
remain inpackages/angular-query-experimental
or any of the React/Solid/Svelte/Vue query packages—rename is complete.docs/framework/react/reference/useMutation.md (2)
117-126
: Confirm optionality consistency for scope in callbacks.Types declare
onSuccess
’sscope
as required, whileonError
/onSettled
acceptscope | undefined
. At runtimescope
can beundefined
ifonMutate
returns nothing. If this is intentional (historic), leave as-is; otherwise consider makingonSuccess
’sscope
optional in docs to reflect reality.Also applies to: 121-124
67-71
: Fix onMutate signature: add MutationFunctionContext param.Docs show only
(variables)
but implementation/types callonMutate(variables, context: MutationFunctionContext)
. Align the signature and mention the param.-`onMutate: (variables: TVariables) => Promise<TScope | void> | TScope | void` +`onMutate: (variables: TVariables, context: MutationFunctionContext) => Promise<TScope | undefined> | TScope | undefined` - Optional - - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive + - Runs before `mutationFn`. Receives the same `variables` and a `context` object with `{ client, mutationKey, meta }`.Likely an incorrect or invalid review comment.
docs/framework/angular/reference/interfaces/basemutationnarrowing.md (2)
6-16
: TContext → TScope rename looks consistent.Interface header and type parameter list are updated correctly.
41-41
: Verify “Defined in” anchors.Line numbers in source links may have shifted with the rename. Please regenerate or validate these anchors to avoid broken/misleading links.
Also applies to: 66-66, 91-91, 116-116
packages/query-core/src/mutationCache.ts (2)
13-26
: Callback param rename to scope matches runtime usage.Config callbacks now receive
scope
(formerlycontext
) and the mutation instance; aligns withmutation.ts
calls.Also applies to: 31-37
96-104
: Injecting client into Mutation is the right move.Passing
client
to the constructor enables buildingMutationFunctionContext
without global access.packages/query-core/src/mutation.ts (4)
18-24
: Config now carries QueryClient; wiring looks correct.Adding
client
toMutationConfig
and storing it privately enables buildingMutationFunctionContext
.Also applies to: 94-96, 99-110
174-179
: Good: centralizes MutationFunctionContext creation.
{ client, meta, mutationKey }
is exactly whatmutationFn
andonMutate
need.
215-218
: Parity with docs: onMutate receives the context.This matches the intended API and should unblock auth/header scenarios.
238-239
: Scope may be undefined at runtime; confirm type intent.
onSuccess
is called withthis.state.context!
. IfonMutate
returned nothing (or on resume without prior context), this isundefined
. Either:
- make
onSuccess
’s third paramTScope | undefined
, or- guarantee a default scope is set before success paths.
Also applies to: 249-250, 263-267, 278-283
packages/query-core/src/types.ts (2)
1094-1099
: LGTM: MutationFunctionContext shape is clear and minimal.Including
client
,meta
, andmutationKey
aligns with the goals. No concerns.
1142-1149
: TScope propagation across public types looks consistent.The generic rename and wiring through options, result types, and unions are coherent.
Also applies to: 1172-1179, 1185-1186, 1316-1319
packages/query-core/src/mutationObserver.ts (2)
16-31
: LGTM: TScope threading and observer plumbing.Listener/result generics, options handling, and state mapping look correct.
Also applies to: 55-79, 87-91, 129-142
144-170
: No additional non-null assertions oncontext
orscope
found. Verified across the repo with ripgrep; the onlycontext!
occurrence remains in packages/query-core/src/mutationObserver.ts.
docs/framework/angular/reference/type-aliases/createmutatefunction.md
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not changed the implementation of MutationObserverResult from context to scope because this would be a breaking change
we should probably deprecate context
in mutationState
and add a scope
field and then remove context
in v6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
packages/query-devtools/src/Devtools.tsx (1)
2419-2431
: UI copy still says “Context” while reading “scope”.Consider renaming the header/label to “Scope” (or “Context (deprecated)”) to avoid confusion during the transition.
- <div class={cx(styles().detailsHeader, 'tsqd-query-details-header')}> - Context Details - </div> + <div class={cx(styles().detailsHeader, 'tsqd-query-details-header')}> + Scope Details + </div> ... - <Explorer - label="Context" + <Explorer + label="Scope" defaultExpanded={['Context']} value={activeMutation()!.state.scope} />packages/query-core/src/__tests__/mutations.test.tsx (1)
22-36
: Add one small test for undefined scope.Edge-case: onMutate returns undefined; verify onSuccess/onSettled receive
undefined
scope.+test('onSuccess receives undefined scope when onMutate returns nothing', async () => { + const onSuccess = vi.fn() + const onSettled = vi.fn() + const mutation = new MutationObserver(queryClient, { + mutationFn: (v: string) => Promise.resolve(v), + onMutate: () => undefined, + onSuccess, + onSettled, + }) + mutation.mutate('x') + await vi.advanceTimersByTimeAsync(0) + expect(onSuccess).toHaveBeenCalledWith('x', 'x', undefined) + expect(onSettled).toHaveBeenCalledWith('x', null, 'x', undefined) +})packages/query-core/src/types.ts (1)
1094-1099
: Minor doc polish for MutationFunctionContext.Add brief JSDoc for client/meta/mutationKey to improve DX.
export type MutationFunctionContext = { - client: QueryClient - meta: MutationMeta | undefined - mutationKey?: MutationKey + /** The QueryClient that scheduled this mutation. */ + client: QueryClient + /** The merged meta from defaults and options for this mutation. */ + meta: MutationMeta | undefined + /** The mutationKey, if provided. */ + mutationKey?: MutationKey }packages/query-core/src/mutation.ts (2)
319-331
: Avoid resetting submittedAt on the second pending dispatch.When onMutate returns a scope, we dispatch
pending
twice and bump submittedAt. Preserve the original timestamp.- case 'pending': - return { + case 'pending': { + const submittedAt = + state.status === 'pending' ? state.submittedAt : Date.now() + return { ...state, context: action.scope, scope: action.scope, data: undefined, failureCount: 0, failureReason: null, error: null, isPaused: action.isPaused, status: 'pending', variables: action.variables, - submittedAt: Date.now(), + submittedAt, } + }
242-254
: Drop non-null assertions onscope
inonSuccess
Remove the unnecessary!
from thescope
argument—types now permitscope | undefined
so the!
is misleading.--- packages/query-core/src/mutation.ts @@ 242c242 - await this.options.onSuccess?.(data, variables, this.state.scope!) + await this.options.onSuccess?.(data, variables, this.state.scope)--- packages/query-core/src/mutationObserver.ts @@ 152c152 - this.#mutateOptions.onSuccess?.(action.data, variables, scope!) + this.#mutateOptions.onSuccess?.(action.data, variables, scope)packages/query-core/src/mutationObserver.ts (1)
144-161
: Drop unnecessary non-null assertion on scope in onSuccess
MutateOptions.onSuccess
acceptsTScope | undefined
;scope!
isn’t needed and is inconsistent with the other callbacks.Apply:
- const scope = this.#currentResult.scope + const scope = this.#currentResult.scope @@ - this.#mutateOptions.onSuccess?.(action.data, variables, scope!) + this.#mutateOptions.onSuccess?.(action.data, variables, scope)Optional (defensive during the transition period): fall back to legacy
context
if ever unset.- const scope = this.#currentResult.scope + const scope = + (this.#currentResult as any).scope ?? + (this.#currentResult as any).context
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
packages/query-core/src/__tests__/mutations.test.tsx
(16 hunks)packages/query-core/src/mutation.ts
(12 hunks)packages/query-core/src/mutationObserver.ts
(8 hunks)packages/query-core/src/types.ts
(8 hunks)packages/query-devtools/src/Devtools.tsx
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
packages/query-core/src/types.ts (1)
packages/query-core/src/mutation.ts (1)
MutationState
(26-45)
packages/query-core/src/__tests__/mutations.test.tsx (1)
packages/query-codemods/src/v5/remove-overloads/utils/index.cjs (1)
scope
(29-31)
packages/query-core/src/mutationObserver.ts (2)
packages/query-core/src/types.ts (3)
MutationObserverResult
(1314-1323)MutationObserverOptions
(1142-1149)MutateOptions
(1151-1173)packages/query-core/src/mutation.ts (4)
Mutation
(88-367)action
(297-366)Action
(78-84)getDefaultState
(369-387)
packages/query-core/src/mutation.ts (4)
packages/query-core/src/queryClient.ts (1)
QueryClient
(61-648)packages/query-core/src/mutationCache.ts (1)
MutationCache
(84-231)packages/query-core/src/types.ts (3)
MutationOptions
(1105-1140)DefaultError
(47-51)MutationFunctionContext
(1094-1098)packages/query-core/src/mutationObserver.ts (3)
MutationObserver
(22-171)action
(144-170)state
(128-142)
🔇 Additional comments (18)
packages/query-devtools/src/Devtools.tsx (1)
2426-2431
: Switched to state.scope — correct and future-proof.Reading scope aligns with core changes and preserves behavior (context still mirrors scope in state).
packages/query-core/src/__tests__/mutations.test.tsx (5)
65-68
: Assertions now include scope alongside context — looks good.Keeping both fields verifies back-compat and the new source of truth.
Also applies to: 94-111, 115-132, 136-153
175-192
: Error-path expectations with scope are consistent.The state transitions and mirrored context/scope fields are asserted correctly.
Also applies to: 196-213, 217-234, 238-255
280-296
: Restore flow with scope — solid coverage.Resume semantics and final success state with scope are validated.
Also applies to: 310-320, 324-336
645-648
: Comment clarifies onMutate may return scope — good guidance.
775-777
: Settled callbacks: scoped argument flows are exercised.Nice coverage for sync/async and ignored return values.
Also applies to: 827-829, 793-797
packages/query-core/src/types.ts (1)
1094-1103
: Scope-threaded mutation typing and MutationFunctionContext — well designed.
- Adds explicit MutationFunctionContext and passes it to mutationFn/onMutate.
- Threads TScope through options, observer results, and mutate overloads.
- Makes scope optional in callbacks, matching runtime realities.
Also applies to: 1114-1133, 1142-1150, 1151-1173, 1175-1183, 1185-1194, 1318-1324
packages/query-core/src/mutation.ts (2)
178-183
: Passing a first-class context object — spot on.Constructing mutationFnContext once and reusing for mutationFn/onMutate is clean and efficient.
Also applies to: 190-191, 219-227
321-323
: Mirroring context <- scope for back-compat — good.Keeps DevTools and users relying on context unbroken.
packages/query-core/src/mutationObserver.ts (9)
16-18
: Rename to TScope in listener looks goodType plumbed through
MutationObserverListener
correctly.
26-31
: Generic rename to TScope + options wiring LGTMDefaulting
TScope = unknown
matches the new API surface.
33-37
: State/result/observer generics aligned
#currentResult
,#currentMutation
, and#mutateOptions
useTScope
consistently.
40-41
: Constructor signature updated correctlyConstructor accepts
MutationObserverOptions<..., TScope>
.
56-79
: setOptions type changes are consistentPrev/next option types use
TScope
; notify-on-change behavior unchanged.
87-91
: Observer update action uses TScope
onMutationUpdate
signature matchesMutation.#dispatch
’sAction<..., TScope>
.
93-101
: getCurrentResult returns TScope-aware resultReturn type is aligned with
MutationObserverResult<..., TScope>
.
112-126
: mutate options now carry scopeSignature matches
MutateOptions<..., TScope>
and integrates withMutationCache.build
.
129-142
: MutationObserverResult includesscope
; nocontext
field present
Accessingthis.#currentResult.scope
is type-safe.
ea2cc38
to
4d34ab6
Compare
Made all the amends @TkDodo so this is ready for another review when you have time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/framework/angular/guides/mutations.md (1)
249-251
: Fix class property access in exampleInside a class method, call the instance property.
- mutation.mutate({ title: 'title' }) + this.mutation.mutate({ title: 'title' })packages/query-core/src/mutationObserver.ts (1)
145-189
: Avoid repeated per-call callbacks and release references; also rename local to reduce confusion.
- Clear #mutateOptions after invoking success/error/settled callbacks to prevent duplicate invocations on subsequent notifications and to drop captured closures.
- Minor: rename local variable
context
tofnContext
to avoid confusion with deprecatedstate.context
.Apply:
if (this.#mutateOptions && this.hasListeners()) { const variables = this.#currentResult.variables! const scope = this.#currentResult.scope - const context = { + const fnContext = { client: this.#client, meta: this.options.meta, mutationKey: this.options.mutationKey, } satisfies MutationFunctionContext if (action?.type === 'success') { - this.#mutateOptions.onSuccess?.( - action.data, - variables, - scope, - context, - ) + this.#mutateOptions.onSuccess?.( + action.data, + variables, + scope, + fnContext, + ) this.#mutateOptions.onSettled?.( action.data, null, variables, scope, - context, + fnContext, ) + this.#mutateOptions = undefined } else if (action?.type === 'error') { - this.#mutateOptions.onError?.(action.error, variables, scope, context) + this.#mutateOptions.onError?.( + action.error, + variables, + scope, + fnContext + ) this.#mutateOptions.onSettled?.( undefined, action.error, variables, scope, - context, + fnContext, ) + this.#mutateOptions = undefined } }
♻️ Duplicate comments (1)
packages/query-core/src/mutation.ts (1)
242-265
: Handlers receive (…, scope, context) as specifiedMatches updated MutationOptions signatures and prior review requests to expose client to all callbacks.
🧹 Nitpick comments (6)
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (2)
5-10
: Assert the shape of MutationFunctionContext (client/meta/mutationKey), not just the alias.Adding a couple of property-level assertions makes the test more robust against accidental shape regressions.
import type { DefaultError, - MutationFunctionContext, + MutationFunctionContext, + MutationMeta, + MutationKey, MutationState, WithRequired, } from '@tanstack/query-core'it('should infer context type correctly', () => { mutationOptions<number>({ mutationFn: (_variables, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() + expectTypeOf(context.client).toEqualTypeOf<QueryClient>() + expectTypeOf(context.meta).toEqualTypeOf<MutationMeta | undefined>() + expectTypeOf(context.mutationKey).toEqualTypeOf<MutationKey | undefined>() return Promise.resolve(5) },Also applies to: 71-76
58-69
: Broaden scope inference checks to all lifecycle callbacks.Verifying scope propagation on onError/onSettled guards against signature drift.
it('should infer scope type correctly', () => { mutationOptions<number, DefaultError, void, { name: string }>({ mutationFn: () => Promise.resolve(5), mutationKey: ['key'], onMutate: () => { return { name: 'scope' } }, onSuccess: (_data, _variables, scope) => { expectTypeOf(scope).toEqualTypeOf<{ name: string } | undefined>() }, + onError: (_e, _v, scope, _context) => { + expectTypeOf(scope).toEqualTypeOf<{ name: string } | undefined>() + }, + onSettled: (_d, _e, _v, scope) => { + expectTypeOf(scope).toEqualTypeOf<{ name: string } | undefined>() + }, }) })Optionally, add a separate test (no generic TScope) to ensure inference from onMutate return:
it('should infer TScope from onMutate return when omitted', () => { mutationOptions({ mutationFn: () => Promise.resolve(5), mutationKey: ['key'], onMutate: () => ({ name: 'scope' as const }), onSuccess: (_d, _v, scope) => { expectTypeOf(scope).toEqualTypeOf<{ name: 'scope' } | undefined>() }, }) })docs/framework/react/guides/optimistic-updates.md (2)
90-92
: Fix doc: scope is the 4th argument, not the lastThe text still says the value from onMutate is passed as the “last argument” to onError/onSettled. With the new API, it’s the 4th arg (scope); the last arg is now the context.
Apply:
- To do this, `useMutation`'s `onMutate` handler option allows you to return a value that will later be passed to both `onError` and `onSettled` handlers as the last argument. + To do this, `useMutation`'s `onMutate` handler option allows you to return a value that will later be passed to both `onError` and `onSettled` as the scope parameter (4th argument).
171-177
: Optional: show context in the onSettled example for consistencyElsewhere on this page you include context; consider adding it here too for uniformity.
- onSettled: async (newTodo, error, variables, scope) => { + onSettled: async (newTodo, error, variables, scope, context) => {docs/framework/angular/guides/optimistic-updates.md (1)
157-163
: Optional: include context param for uniformityConsider adding context to this onSettled signature as shown above for consistency.
- onSettled: (newTodo, error, variables, scope) => { + onSettled: (newTodo, error, variables, scope, context) => {packages/query-core/src/mutation.ts (1)
178-183
: Consider freezing the context object before exposing to user codePrevents accidental mutation of client/meta. Low risk, optional.
- const mutationFnContext = { + const mutationFnContext = Object.freeze({ client: this.#client, meta: this.options.meta, mutationKey: this.options.mutationKey, - } satisfies MutationFunctionContext + }) satisfies MutationFunctionContext
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
docs/framework/angular/guides/mutations.md
(4 hunks)docs/framework/angular/guides/optimistic-updates.md
(3 hunks)docs/framework/react/guides/mutations.md
(4 hunks)docs/framework/react/guides/optimistic-updates.md
(3 hunks)examples/react/optimistic-updates-cache/src/pages/index.tsx
(3 hunks)packages/query-core/src/mutation.ts
(12 hunks)packages/query-core/src/mutationObserver.ts
(8 hunks)packages/query-core/src/types.ts
(8 hunks)packages/react-query/src/__tests__/mutationOptions.test-d.tsx
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/query-core/src/types.ts
- examples/react/optimistic-updates-cache/src/pages/index.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
packages/query-core/src/mutationObserver.ts (2)
packages/query-core/src/types.ts (3)
MutationObserverOptions
(1145-1152)MutateOptions
(1154-1179)MutationFunctionContext
(1094-1098)packages/query-core/src/mutation.ts (4)
Mutation
(88-380)action
(310-379)Action
(78-84)getDefaultState
(382-400)
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (3)
packages/angular-query-experimental/src/mutation-options.ts (1)
mutationOptions
(90-99)packages/react-query/src/mutationOptions.ts (1)
mutationOptions
(29-38)packages/query-core/src/types.ts (2)
DefaultError
(47-51)MutationFunctionContext
(1094-1098)
packages/query-core/src/mutation.ts (4)
packages/query-core/src/queryClient.ts (1)
QueryClient
(61-648)packages/query-core/src/mutationCache.ts (1)
MutationCache
(84-231)packages/query-core/src/types.ts (3)
MutationOptions
(1105-1143)DefaultError
(47-51)MutationFunctionContext
(1094-1098)packages/query-core/src/mutationObserver.ts (3)
MutationObserver
(23-190)action
(145-189)state
(129-143)
🔇 Additional comments (29)
docs/framework/react/guides/optimistic-updates.md (3)
102-115
: Good: context passed to onMutate; scope returned for later callbacksThe example correctly uses context.client and returns a scope object for rollback.
118-124
: Good: onError/onSettled arity reflects new APISignatures and usage of scope + context look correct.
137-159
: Consistent usage: context.client for cache ops; scope carries snapshotLooks good and aligns with types.
docs/framework/react/guides/mutations.md (5)
146-162
: Correct scope-based lifecycle in examplesonMutate returns a scope; subsequent handlers accept scope. Matches the updated API.
192-214
: Consistent handler overrides with scope in mutate optionsSignatures are correct and mirror observer notifications.
229-243
: Consecutive mutations example reflects new scope paramAccurately documents single-fire behavior for per-call callbacks.
297-321
: Good: defaults example uses context.client and returns scopeReads well and demonstrates the recommended pattern for optimistic updates.
395-402
: Scope queueing docs look accurateThe new scope.id serialization behavior is described clearly.
docs/framework/angular/guides/optimistic-updates.md (3)
90-103
: Good: onMutate receives context; returns scopeMatches the React counterpart and type signatures.
106-113
: Good: onError/onSettled use scope + contextRollback and invalidation patterns are correct.
125-148
: Good: single-item optimistic flow mirrors the list exampleUse of scope.newTodo.id and previousTodo is consistent.
docs/framework/angular/guides/mutations.md (4)
93-110
: Correct: scope lifecycle demonstrated with Angular injectMutationHandlers accept scope; messaging is clear.
133-142
: Consistent handler overrides include scopeMatches global behavior.
164-173
: Consecutive mutations: scope signature is correctExample aligns with observer semantics.
217-243
: Good: defaults use context.client; scope carries optimistic itemAccurate and practical.
packages/query-core/src/mutation.ts (7)
18-24
: Public config now injects QueryClient — good directionPassing client via config unblocks building a proper MutationFunctionContext.
32-37
: Back-compat: keep state.context while introducing state.scopeNice deprecation path by mirroring scope into context.
98-115
: Observer/client wiring looks correctStoring #client and defaulting state via getDefaultState is fine.
219-230
: Correctly await onMutate, then propagate returned scope to stateReference equality check avoids redundant dispatches. Looks good.
310-336
: Reducer mirrors scope into deprecated context for transitionEnsures existing consumers of state.context don’t break. Good.
382-400
: Default state includes both context (deprecated) and scopeConsistent with reducer semantics.
190-191
: No changes required: MutationFunction already includes context
TheMutationFunction<TData, TVariables>
signature inpackages/query-core/src/types.ts
defines a secondcontext
parameter, andoptions.mutationFn
inmutation.ts
is invoked with bothvariables
andmutationFnContext
.packages/query-core/src/mutationObserver.ts (7)
9-12
: Import of MutationFunctionContext is correct and aligns with callback signatures.
17-19
: TScope migration across listener, class, and fields looks consistent.
Covers listener type, class generics/extends, options, currentResult/currentMutation/mutateOptions.Also applies to: 23-31, 34-38
56-80
: Option typing and observerOptionsUpdated notification path remain intact after TScope migration.
No issues spotted.
88-92
: Observer update flow intact.
Type alignment on Action and call sequence (#updateResult then #notify) is good.
94-101
: Result typing update LGTM.
Return type now parameterized by TScope; consistent with state shape carrying both scope and deprecated context.
112-127
: Mutate signature/update path LGTM.
Per-call MutateOptions now parameterized by TScope and correctly forwarded via execute().
129-143
: Result assembly preserves back-compat.
Spreading state (which includes both scope and deprecated context) and narrowing to MutationObserverResult is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/framework/angular/guides/mutations.md (1)
245-253
: Broken snippet: missingthis.
when callingmutate
Inside a class method,
mutation
must be referenced viathis
.class someComponent { // Start mutation in some component: mutation = injectMutation(() => ({ mutationKey: ['addTodo'] })) someMethod() { - mutation.mutate({ title: 'title' }) + this.mutation.mutate({ title: 'title' }) } }
♻️ Duplicate comments (2)
docs/framework/react/guides/mutations.md (1)
146-162
: Nice: callbacks now include both scope and contextIncluding
context
as the final argument to callbacks (alongsidescope
) aligns with the new API and resolves the earlier request to documentcontext
here.docs/framework/react/reference/useMutation.md (1)
67-72
: Inconsistent signature: onMutate is missing thecontext
parameterGuides show
onMutate: (variables, context) => ...
, andcontext
is required to accessQueryClient
insideonMutate
. Update the reference to match.-- `onMutate: (variables: TVariables) => Promise<TScope | void> | TScope | void` +- `onMutate: (variables: TVariables, context: MutationFunctionContext) => Promise<TScope | void> | TScope | void` - Optional - - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive + - This function fires before `mutationFn` and receives the same `variables` plus the `context` (with `QueryClient`, `mutationKey`, optional `meta`) - Useful to perform optimistic updates to a resource in hopes that the mutation succeeds - The value returned from this function will be passed to both the `onError` and `onSettled` functions in the event of a mutation failure and can be useful for rolling back optimistic updates.
🧹 Nitpick comments (3)
packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (1)
75-94
: Validate context shape, not just the aliasGreat check for
MutationFunctionContext
across all call sites. Consider asserting the known fields to catch regressions to its structure.Apply this diff to strengthen the assertions:
it('should infer context type correctly', () => { mutationOptions<number>({ mutationFn: (_variables, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() + expectTypeOf(context.client).toEqualTypeOf<QueryClient>() + expectTypeOf(context.meta).toEqualTypeOf<MutationFunctionContext['meta']>() + expectTypeOf(context.mutationKey).toEqualTypeOf<MutationFunctionContext['mutationKey']>() return Promise.resolve(5) }, mutationKey: ['key'], onMutate: (_variables, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() + expectTypeOf(context.client).toEqualTypeOf<QueryClient>() }, onSuccess: (_data, _variables, _scope, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() }, onError: (_error, _variables, _scope, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() }, onSettled: (_data, _error, _variables, _scope, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() }, }) })docs/framework/angular/guides/mutations.md (1)
245-245
: PascalCase class name (style)Angular classes conventionally use PascalCase.
-class someComponent { +class SomeComponent {docs/framework/react/reference/useMutation.md (1)
51-56
: Minor wording/punctuation tweak for clarityImprove readability per grammar hint.
- - `variables` is an object that `mutate` will pass to your `mutationFn` - - `context` is an object that `mutate` will pass to your `mutationFn`. Contains reference to `QueryClient`, `mutationKey` and optional `meta` object. + - `variables`: the object that `mutate` passes to your `mutationFn` + - `context`: an object passed to your `mutationFn` containing a reference to the `QueryClient`, the `mutationKey`, and an optional `meta` object
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
docs/framework/angular/guides/mutations.md
(4 hunks)docs/framework/react/guides/mutations.md
(4 hunks)docs/framework/react/reference/useMutation.md
(3 hunks)packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts
(2 hunks)packages/query-core/src/mutation.ts
(12 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/query-core/src/mutation.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (2)
packages/angular-query-experimental/src/mutation-options.ts (1)
mutationOptions
(90-99)packages/query-core/src/types.ts (2)
DefaultError
(47-51)MutationFunctionContext
(1094-1098)
🪛 LanguageTool
docs/framework/react/reference/useMutation.md
[grammar] ~53-~53: There might be a mistake here.
Context: ...asynchronous task and returns a promise. - variables
is an object that mutate
will pass to...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...pe | void> | TScope | void` - Optional - This function will fire before the mutat...
(QB_NEW_EN)
🔇 Additional comments (2)
packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (2)
11-11
: Good addition: importing MutationFunctionContextImporting from core keeps the test source-of-truth aligned with shared types.
62-73
: Scope inference looks correctAsserting
TScope | undefined
on lifecycle callbacks matches the optional return fromonMutate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/query-core/src/__tests__/hydration.test.tsx (1)
868-871
: Bug: post-hydration assertion targets the dehydrated payload instead of the hydrated cacheAfter
hydrate(hydrationClient, parsed)
, the second assertion still inspectsdehydrated
. It won’t catch regressions in hydration.Apply:
- expect(dehydrated.mutations[0]?.scope?.id).toBe('scope') + expect( + hydrationCache.find({ mutationKey: ['mutation'] })?.scope?.id + ).toBe('scope')
🧹 Nitpick comments (17)
packages/solid-query/src/__tests__/useMutation.test.tsx (5)
586-590
: Use partial matcher for scope to reduce brittleness.Future additions to the scope object could break this strict deep-equality check. Match only what matters (client) with
expect.objectContaining(...)
.- expect(onMutate).toHaveBeenCalledWith('todo', { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onMutate).toHaveBeenCalledWith( + 'todo', + expect.objectContaining({ client: queryClient }) + )
1060-1064
: Relax scope matching in onSuccessMutate assertion.Avoid coupling tests to exact scope shape; assert the presence of the client only.
- expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onSuccessMutate).toHaveBeenCalledWith( + 'result2', + 'todo', + undefined, + expect.objectContaining({ client: queryClient }) + )
1071-1075
: Relax scope matching in onSettledMutate assertion.- { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }, + expect.objectContaining({ client: queryClient }),
1114-1118
: Relax scope matching in onError assertion.- expect(onError).toHaveBeenCalledWith(error, 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + error, + 'todo', + undefined, + expect.objectContaining({ client: queryClient }) + )
1203-1207
: Relax scope matching in onError assertion (settled error path).- expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + mutateFnError, + 'todo', + undefined, + expect.objectContaining({ client: queryClient }) + )If helpful, I can add a targeted test ensuring mutationFn receives the new second-arg scope and that mutateAsync overrides also receive scope.
packages/query-core/src/__tests__/hydration.test.tsx (1)
544-549
: Harden assertion for new 4th onSuccess parameter to avoid brittle deep equalityThe mutation object may gain fields over time. Matching the exact shape makes this test fragile.
Apply:
- expect(clientOnSuccess).toHaveBeenCalledWith( - { id: 2, text: 'text' }, - { text: 'text' }, - { optimisticTodo: { id: 1, text: 'text' } }, - { client: client, meta: undefined, mutationKey: ['addTodo'] }, - ) + expect(clientOnSuccess).toHaveBeenCalledWith( + { id: 2, text: 'text' }, + { text: 'text' }, + { optimisticTodo: { id: 1, text: 'text' } }, + expect.objectContaining({ client, meta: undefined, mutationKey: ['addTodo'] }), + )packages/query-core/src/__tests__/mutationObserver.test.tsx (4)
334-339
: Use partial match for scope in onSettled (success path).Prevents test breakage if scope structure evolves.
- undefined, - { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }, + undefined, + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }),
366-370
: Use partial match for scope in onError (error path).Same rationale: keep assertions resilient to additional scope fields.
- expect(onError).toHaveBeenCalledWith(error, 'error', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + error, + 'error', + undefined, + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }), + )
372-382
: Use partial match for scope in onSettled (error path).Aligns all callback assertions and reduces coupling to scope’s exact shape.
- expect(onSettled).toHaveBeenCalledWith( - undefined, - error, - 'error', - undefined, - { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }, - ) + expect(onSettled).toHaveBeenCalledWith( + undefined, + error, + 'error', + undefined, + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }), + )
323-327
: Future-proof the assertion by shape-matching the context argument
Useexpect.objectContaining
for the fourth argument to match only the current fields (client
,meta
,mutationKey
), avoiding brittle tests if new fields (e.g.,signal
) are added or ordering changes:- expect(onSuccess).toHaveBeenCalledWith('SUCCESS', 'success', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onSuccess).toHaveBeenCalledWith( + 'SUCCESS', + 'success', + undefined, + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }), + )[current
MutationFunctionContext
fields:client
,meta
,mutationKey
]packages/react-query/src/__tests__/useMutation.test.tsx (7)
528-532
: Use partial matching for scope object to avoid brittle assertionsFuture additions to the mutation scope may break strict deep-equality. Prefer
expect.objectContaining
to assert only relevant fields.- expect(onMutate).toHaveBeenCalledWith('todo', { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onMutate).toHaveBeenCalledWith( + 'todo', + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }), + )
986-991
: Make onSuccess assertion resilient to scope shape changesUse partial matching for the scope object.
- undefined, - { + undefined, + expect.objectContaining({ client: queryClient, meta: undefined, mutationKey: undefined, - }, + }),
997-1002
: Same here: prefer partial match for scope- undefined, - { + undefined, + expect.objectContaining({ client: queryClient, meta: undefined, mutationKey: undefined, - }, + }),
1009-1014
: Use partial matching for mutate-level onSuccess scope- undefined, - { + undefined, + expect.objectContaining({ client: queryClient, meta: undefined, mutationKey: undefined, - }, + }),
1021-1026
: Use partial matching for mutate-level onSettled scope- undefined, - { + undefined, + expect.objectContaining({ client: queryClient, meta: undefined, mutationKey: undefined, - }, + }),
1060-1064
: Use partial matching for onError scope- expect(onError).toHaveBeenCalledWith(error, 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + error, + 'todo', + undefined, + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }), + )
1141-1145
: Use partial matching for onError scope (settled-error path)- expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + mutateFnError, + 'todo', + undefined, + expect.objectContaining({ + client: queryClient, + meta: undefined, + mutationKey: undefined, + }), + )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
packages/query-core/src/__tests__/hydration.test.tsx
(1 hunks)packages/query-core/src/__tests__/mutationObserver.test.tsx
(2 hunks)packages/query-core/src/__tests__/mutations.test.tsx
(17 hunks)packages/react-query/src/__tests__/useMutation.test.tsx
(4 hunks)packages/solid-query/src/__tests__/useMutation.test.tsx
(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/query-core/src/tests/mutations.test.tsx
View your CI Pipeline Execution ↗ for commit 88b9e83
☁️ Nx Cloud last updated this comment at |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #9615 +/- ##
===========================================
+ Coverage 45.47% 61.71% +16.24%
===========================================
Files 209 157 -52
Lines 8372 6162 -2210
Branches 1905 1722 -183
===========================================
- Hits 3807 3803 -4
+ Misses 4118 2033 -2085
+ Partials 447 326 -121 🚀 New features to boost your workflow:
|
@TkDodo this is ready for review, i've looked at the tests and got the CI green 👍🏼 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
docs/framework/react/reference/useMutation.md (1)
72-76
: Context param now documented in callbacks — resolves prior concern.The
context: MutationFunctionContext
parameter is included here. This addresses the earlier reviewer note.
🧹 Nitpick comments (2)
docs/framework/react/reference/useMutation.md (2)
51-56
: Clarify MutationFunctionContext wording and tighten phrasing.Rephrase the context note for precision and style.
Apply:
- - `context` is an object that `mutate` will pass to your `mutationFn`. Contains reference to `QueryClient`, `mutationKey` and optional `meta` object. + - `context` is an object that `mutate` will pass to your `mutationFn`. It contains a reference to the `QueryClient`, the `mutationKey`, and an optional `meta` object.
76-79
: Note possible undefined scope to set expectations.Call out that
scope
may beundefined
ifonMutate
returnsvoid
.Apply:
- - This function will fire if the mutation encounters an error and will be passed the error. + - This function will fire if the mutation encounters an error and will be passed the error. Note: `scope` will be `undefined` if `onMutate` returned `void`.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
docs/framework/react/reference/useMutation.md
(3 hunks)packages/query-core/src/utils.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/query-core/src/utils.ts
🧰 Additional context used
🪛 LanguageTool
docs/framework/react/reference/useMutation.md
[grammar] ~53-~53: There might be a mistake here.
Context: ...asynchronous task and returns a promise. - variables
is an object that mutate
will pass to...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...pe | void> | TScope | void` - Optional - This function will fire before the mutat...
(QB_NEW_EN)
🔇 Additional comments (4)
docs/framework/react/reference/useMutation.md (4)
80-83
: LGTM — signature parity and parameter order are consistent.
117-121
: LGTM — mutate options mirror the top-level options (incl. context).
121-124
: LGTM — mutate onError includes scope and context as intended.
125-128
: LGTM — mutate onSettled signature matches the documented callbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh noo, I forgot that we also have a MutationScope already 😭
query/packages/query-core/src/types.ts
Lines 1082 to 1084 in 23c8908
export type MutationScope = { | |
id: string | |
} |
It can be passed as an argument to mutations. So, we need to find a different name again 😅
We could append |
Or |
we also have a |
yeah |
yeah that’s not bad, I think |
Do you think Also i agree, it's important especially because it's in the docs & helps developers learn 😄 |
alt. name |
what do you think about just |
I like it, explicit. Agreed, if it were an object it'd be a mouthful but positionally & for documentation wise its clear. I'll make that change. |
Co-Authored-By: travikk <[email protected]>
Co-Authored-By: travikk <[email protected]>
Co-Authored-By: Dominik Dorfmeister <[email protected]>
88b9e83
to
1c5a820
Compare
Okay that's been done & i've rebased with latest updates – think i've caught everything... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
docs/framework/angular/reference/functions/injectmutation.md (1)
23-23
: Fix default TError to DefaultErrorAngular uses DefaultError by default; docs currently show Error.
-• **TError** = `Error` +• **TError** = `DefaultError`docs/framework/react/guides/mutations.md (1)
254-264
: Fix undefined input variable in mutateAsync example.The snippet references “todo” before defining it. Provide an input object to avoid confusion.
-try { - const todo = await mutation.mutateAsync(todo) +try { + const todo = await mutation.mutateAsync({ title: 'Do Laundry' }) console.log(todo) } catch (error) {docs/framework/angular/guides/mutations.md (1)
251-253
: Fix instance method callInside the class, call the instance property:
this.mutation.mutate(...)
.Apply this diff:
- mutation.mutate({ title: 'title' }) + this.mutation.mutate({ title: 'title' })
♻️ Duplicate comments (5)
docs/framework/angular/reference/type-aliases/createmutatefunction.md (1)
6-10
: Fix untyped rest parameter in alias to mirror MutateFunction-type CreateMutateFunction<TData, TError, TVariables, TScope> = (...args) => void +type CreateMutateFunction<TData, TError, TVariables, TScope> = ( + ...args: Parameters<MutateFunction<TData, TError, TVariables, TScope>> +) => voidRun to verify the source type (docs are generated):
#!/bin/bash # Verify CreateMutateFunction is using a typed rest parameter in source fd -a types.ts packages/angular-query-experimental | while read f; do echo "== $f ==" rg -nP 'type\s+CreateMutateFunction<[^>]+>\s*=\s*\(' -n "$f" -C2 rg -nP '\.\.\.args:\s*Parameters<MutateFunction<[^>]+>>' -n "$f" -C2 doneAlso applies to: 20-27
docs/framework/svelte/reference/functions/createmutation.md (1)
19-19
: Fix default TError in Svelte docs to DefaultErrorThis mirrors prior feedback on related pages.
-• **TError** = `Error` +• **TError** = `DefaultError`docs/framework/react/guides/mutations.md (2)
190-214
: Fix undefined identifier: mutate is never declared.Destructure mutate from useMutation in the example so readers don’t hit a ReferenceError.
-useMutation({ +const { mutate } = useMutation({ mutationFn: addTodo, - onSuccess: (data, variables, onMutateResult, context) => { + onSuccess: (data, variables, onMutateResult, context) => { // I will fire first }, - onError: (error, variables, onMutateResult, context) => { + onError: (error, variables, onMutateResult, context) => { // I will fire first }, - onSettled: (data, error, variables, onMutateResult, context) => { + onSettled: (data, error, variables, onMutateResult, context) => { // I will fire first }, }) mutate(todo, { onSuccess: (data, variables, onMutateResult, context) => { // I will fire second! }, onError: (error, variables, onMutateResult, context) => { // I will fire second! }, onSettled: (data, error, variables, onMutateResult, context) => { // I will fire second! }, })
227-243
: Fix undefined identifier in “Consecutive mutations”.Same problem: declare mutate via useMutation before calling it.
-useMutation({ +const { mutate } = useMutation({ mutationFn: addTodo, onSuccess: (data, variables, onMutateResult, context) => { // Will be called 3 times }, }) const todos = ['Todo 1', 'Todo 2', 'Todo 3'] todos.forEach((todo) => { mutate(todo, { onSuccess: (data, variables, onMutateResult, context) => { // Will execute only once, for the last mutation (Todo 3), // regardless which mutation resolves first }, }) })docs/framework/react/reference/useMutation.md (1)
67-71
: Add context parameter to onMutate signature and update wording.onMutate accepts (variables, context), and its return is forwarded to all three callbacks.
-- `onMutate: (variables: TVariables) => Promise<TScope | void> | TScope | void` +- `onMutate: (variables: TVariables, context: MutationFunctionContext) => Promise<TScope | void> | TScope | void` - Optional - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive - Useful to perform optimistic updates to a resource in hopes that the mutation succeeds - - The value returned from this function will be passed to both the `onError` and `onSettled` functions in the event of a mutation failure and can be useful for rolling back optimistic updates. + - The value returned from this function will be passed to `onSuccess`, `onError`, and `onSettled`. This can be used for optimistic updates and rollbacks.
🧹 Nitpick comments (27)
packages/solid-query/src/__tests__/useMutation.test.tsx (2)
586-590
: Future-proof callback-context assertions by partially matching the objectAvoid brittle tests that hardcode every field of the context object. Match only what you care about (client), so additional fields won’t break tests.
Apply this diff:
@@ - expect(onMutate).toHaveBeenCalledTimes(1) - expect(onMutate).toHaveBeenCalledWith('todo', { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onMutate).toHaveBeenCalledTimes(1) + expect(onMutate).toHaveBeenCalledWith( + 'todo', + expect.objectContaining({ client: queryClient }), + ) @@ - expect(onSuccessMutate).toHaveBeenCalledTimes(1) - expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onSuccessMutate).toHaveBeenCalledTimes(1) + expect(onSuccessMutate).toHaveBeenCalledWith( + 'result2', + 'todo', + undefined, + expect.objectContaining({ client: queryClient }), + ) @@ - expect(onSettledMutate).toHaveBeenCalledTimes(1) - expect(onSettledMutate).toHaveBeenCalledWith( - 'result2', - null, - 'todo', - undefined, - { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }, - ) + expect(onSettledMutate).toHaveBeenCalledTimes(1) + expect(onSettledMutate).toHaveBeenCalledWith( + 'result2', + null, + 'todo', + undefined, + expect.objectContaining({ client: queryClient }), + ) @@ - expect(onError).toHaveBeenCalledWith(error, 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + error, + 'todo', + undefined, + expect.objectContaining({ client: queryClient }), + ) @@ - expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined, { - client: queryClient, - meta: undefined, - mutationKey: undefined, - }) + expect(onError).toHaveBeenCalledWith( + mutateFnError, + 'todo', + undefined, + expect.objectContaining({ client: queryClient }), + )Also applies to: 1060-1064, 1071-1076, 1114-1118, 1203-1207
1004-1077
: Add a focused test that validates onMutateResult is threaded into callbacksCurrently, assertions only cover the new context object; they don’t exercise that the onMutate return value is forwarded as onMutateResult. Add a narrow test to lock this in.
Example test to append at the end of the file:
it('should pass onMutateResult to onSuccess/onSettled callbacks', async () => { const onSuccess = vi.fn() const onSettled = vi.fn() function Page() { const mutation = useMutation(() => ({ mutationFn: async (v: number) => v * 2, onMutate: (v: number) => `optimistic:${v}`, onSuccess, onSettled, })) createEffect(() => { setActTimeout(() => mutation.mutate(2), 0) }) return null } render(() => ( <QueryClientProvider client={queryClient}> <Page /> </QueryClientProvider> )) await vi.advanceTimersByTimeAsync(0) expect(onSuccess).toHaveBeenCalledWith( 4, 2, 'optimistic:2', expect.any(Object), ) expect(onSettled).toHaveBeenCalledWith( 4, null, 2, 'optimistic:2', expect.any(Object), ) })packages/solid-query/src/types.ts (1)
143-159
: Document what “TScope” represents to reduce ambiguityA short JSDoc on TScope (e.g., “additional runtime scope passed to mutateFn and callbacks; contains client, meta, mutationKey”) will help users migrating from “context”.
Suggested addition:
-export interface SolidMutationOptions< +export interface SolidMutationOptions< TData = unknown, TError = DefaultError, TVariables = void, - TScope = unknown, + /** + * Additional runtime scope passed to mutateFn and callbacks. + * Typically includes { client, meta, mutationKey } from query-core. + */ + TScope = unknown, > extends OmitKeyof<packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts (1)
146-149
: Align callback parameter lists with new onMutateResult signatureUpdate the examples to include the onMutateResult parameter and keep its position consistent: onError(error, variables, onMutateResult, scope) and onSettled(data, error, variables, onMutateResult, scope).
- return 'onError: (error, variables, scope) => {\n console.log("error:", error, "scope:", scope)\n}' + return 'onError: (error, variables, onMutateResult, scope) => {\n console.log("error:", error, "scope:", scope, "onMutateResult:", onMutateResult)\n}' - return 'onSettled: (data, error, variables, scope) => {\n console.log("settled", scope)\n}' + return 'onSettled: (data, error, variables, onMutateResult, scope) => {\n console.log("settled", scope, onMutateResult)\n}'- onError: (error, variables, scope) => { - console.log(error, scope) + onError: (error, variables, onMutateResult, scope) => { + console.log(error, scope, onMutateResult) }, - onSettled: (data, error, variables, scope) => { - console.log('settled', scope) + onSettled: (data, error, variables, onMutateResult, scope) => { + console.log('settled', scope, onMutateResult) },Also applies to: 205-210
packages/query-devtools/src/Devtools.tsx (1)
2419-2431
: Rename UI to “Scope” and keep a fallback to legacy state.context.This prevents user confusion and preserves visibility for hydrated mutations that still carry context.
Apply:
- <div class={cx(styles().detailsHeader, 'tsqd-query-details-header')}> - Context Details + <div class={cx(styles().detailsHeader, 'tsqd-query-details-header')}> + Scope Details @@ - <Explorer - label="Context" + <Explorer + label="Scope" @@ - value={activeMutation()!.state.scope} + value={ + activeMutation()!.state.scope ?? + // fallback for older persisted entries + // @ts-expect-error legacy + activeMutation()!.state.context + }To ensure nothing else still reads state.context:
#!/bin/bash rg -nS '\bstate\.context\b|Context Details|label="Context"' packages/query-devtools || truepackages/vue-query/src/mutationCache.ts (1)
10-19
: Use the TypeScriptoverride
modifier on overridden methods.Improves safety against base-class signature drift.
export class MutationCache extends MC { - find< + override find< TData = unknown, TError = DefaultError, TVariables = any, TScope = unknown, >( filters: MaybeRefDeep<MutationFilters>, ): Mutation<TData, TError, TVariables, TScope> | undefined { return super.find(cloneDeepUnref(filters)) }packages/query-core/src/utils.ts (1)
173-200
: Preserve type inference in matchMutation by threading generics.Expose the same generics as MutationFilters/Mutation so predicates and call sites retain concrete types instead of falling back to
any
.-export function matchMutation( - filters: MutationFilters, - mutation: Mutation<any, any>, -): boolean { +export function matchMutation< + TData = unknown, + TError = DefaultError, + TVariables = unknown, + TScope = unknown, +>( + filters: MutationFilters<TData, TError, TVariables, TScope>, + mutation: Mutation<TData, TError, TVariables, TScope>, +): boolean {No behavioral change; improves editor IntelliSense and catches mismatches in predicates.
packages/query-core/src/queryClient.ts (1)
529-546
: Optional: surface generics in getMutationDefaults for better inference.Currently returns
any
-based generics; consider mirroringsetMutationDefaults
generics for callers who carry through concrete T types.-export function getMutationDefaults( - mutationKey: MutationKey, -): OmitKeyof<MutationObserverOptions<any, any, any, any>, 'mutationKey'> { +export function getMutationDefaults< + TData = unknown, + TError = DefaultError, + TVariables = void, + TScope = unknown, +>( + mutationKey: MutationKey, +): OmitKeyof<MutationObserverOptions<TData, TError, TVariables, TScope>, 'mutationKey'> {packages/vue-query/src/queryClient.ts (1)
472-476
: Stronger typing for getMutationDefaults (optional)Expose generics here too to mirror setMutationDefaults and preserve TScope across reads.
- getMutationDefaults( - mutationKey: MaybeRefDeep<MutationKey>, - ): MutationObserverOptions<any, any, any, any> { + getMutationDefaults< + TData = unknown, + TError = DefaultError, + TVariables = void, + TScope = unknown + >( + mutationKey: MaybeRefDeep<MutationKey>, + ): MutationObserverOptions<TData, TError, TVariables, TScope> {examples/react/optimistic-updates-cache/src/pages/index.tsx (2)
9-10
: Remove unused useQueryClient import and variableNot used after switching to context.client.
useQuery, - useQueryClient, } from '@tanstack/react-query' @@ - const queryClient = useQueryClient()Also applies to: 35-35
75-86
: Prefer todoListOptions.queryKey for consistencyUse the same queryKey source everywhere to avoid drift.
- if (onMutateResult?.previousTodos) { - context.client.setQueryData<Todos>( - ['todos'], - onMutateResult.previousTodos, - ) - } + if (onMutateResult?.previousTodos) { + context.client.setQueryData<Todos>( + todoListOptions.queryKey, + onMutateResult.previousTodos, + ) + } @@ - onSettled: (data, error, variables, onMutateResult, context) => - context.client.invalidateQueries({ queryKey: ['todos'] }), + onSettled: (data, error, variables, onMutateResult, context) => + context.client.invalidateQueries({ queryKey: todoListOptions.queryKey }),docs/framework/angular/reference/functions/mutationoptions.md (1)
49-49
: Clarify what TScope represents in the docs.Readers may confuse callback “context” with the new scope type. Consider expanding the bullet to define TScope explicitly.
-• **TScope** = `unknown` +• **TScope** = `unknown` — the type of the value returned from `onMutate` (often called the “onMutateResult”) that is passed to `onSuccess`, `onError`, and `onSettled`.docs/framework/react/guides/optimistic-updates.md (2)
102-106
: Add a brief hint that “context” is a MutationFunctionContext.Helps readers understand where
client
comes from without hunting.useMutation({ mutationFn: updateTodo, // When mutate is called: - onMutate: async (newTodo, context) => { + // context is a MutationFunctionContext (client, meta, mutationKey) + onMutate: async (newTodo, context) => {Also applies to: 108-115, 117-124
137-147
: Mirror the same context hint in the second example.Keeps both examples self-explanatory.
useMutation({ mutationFn: updateTodo, // When mutate is called: - onMutate: async (newTodo, context) => { + // context is a MutationFunctionContext (client, meta, mutationKey) + onMutate: async (newTodo, context) => {Also applies to: 149-156, 159-161
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (1)
74-91
: Strengthen context shape assertions.Also assert key properties on the context to lock its public surface.
it('should infer context type correctly', () => { mutationOptions<number>({ mutationFn: (_variables, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() + expectTypeOf(context.client).toMatchTypeOf<QueryClient>() + expectTypeOf(context.meta).toEqualTypeOf<Record<string, unknown> | undefined>() + expectTypeOf(context.mutationKey).toEqualTypeOf<unknown[] | undefined>() return Promise.resolve(5) }, mutationKey: ['key'], onMutate: (_variables, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() + expectTypeOf(context.client.cancelQueries).toBeFunction() }, onSuccess: (_data, _variables, _onMutateResult, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() }, onError: (_error, _variables, _onMutateResult, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() }, onSettled: (_data, _error, _variables, _onMutateResult, context) => { expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() }, }) })docs/framework/svelte/reference/type-aliases/createbasemutationresult.md (1)
34-34
: Clarify what TScope represents in the docsAdd a short note so users understand provenance and flow.
Apply this doc tweak:
-• **TScope** = `unknown` +• **TScope** = `unknown` — value returned from `onMutate`, passed as the 4th arg to callbacks (`onError`, `onSuccess`, `onSettled`), and stored on `mutation.state.scope` (legacy `context` is deprecated).packages/query-core/src/__tests__/mutations.test.tsx (3)
56-60
: Make MutationFunctionContext assertion future-prooftoHaveBeenCalledWith on a concrete object is brittle if more fields (e.g., signal) are added.
Apply:
- expect(fn).toHaveBeenCalledWith('vars', { - client: queryClient, - meta: undefined, - mutationKey: key, - }) + expect(fn).toHaveBeenCalledWith( + 'vars', + expect.objectContaining({ + client: queryClient, + mutationKey: key, + meta: undefined, + }), + )
71-72
: Scope state assertions look good; consider DRY helperAsserting both context and scope across many states is repetitive. A tiny helper would reduce noise and make future changes (e.g., dropping legacy context) easier.
Example helper (outside the test blocks):
function expectStateScopeEqual(state: { scope?: any; context?: any }, expected: any) { expect(state.scope).toEqual(expected) expect(state.context).toEqual(expected) // until legacy is removed }Then replace duplicated pairs with one call.
Also applies to: 100-101, 121-122, 142-143, 181-182, 202-203, 223-224, 244-245, 285-286, 299-300, 315-316, 330-331
796-796
: Remove stray console.log in testAvoids noisy CI output.
- console.log(results) + // (removed) console.log(results)packages/vue-query/src/useMutation.ts (1)
27-34
: Consider aligning the generic name with core (optional).To reduce cognitive load alongside “Mutation Scopes” (scope: { id }), consider renaming local generic TScope to TOnMutateResult for symmetry with @tanstack/query-core.
-type MutationResult<TData, TError, TVariables, TScope> = … -type UseMutationOptionsBase<TData, TError, TVariables, TScope> = … -export type UseMutationOptions<…, TScope = unknown> = … -type MutateSyncFunction<…, TScope = unknown> = … -export type UseMutationReturnType<…, TScope, TResult = MutationResult<…, TScope>> = … -export function useMutation<…, TScope = unknown>(…): UseMutationReturnType<…, TScope> { +type MutationResult<TData, TError, TVariables, TOnMutateResult> = … +type UseMutationOptionsBase<TData, TError, TVariables, TOnMutateResult> = … +export type UseMutationOptions<…, TOnMutateResult = unknown> = … +type MutateSyncFunction<…, TOnMutateResult = unknown> = … +export type UseMutationReturnType<…, TOnMutateResult, TResult = MutationResult<…, TOnMutateResult>> = … +export function useMutation<…, TOnMutateResult = unknown>(…): UseMutationReturnType<…, TOnMutateResult> {Also applies to: 39-41, 55-61
docs/framework/angular/guides/mutations.md (2)
225-241
: Guard against undefined cache data in setQueryData updatersIf
old
is undefined, spreading or calling.map/.filter
throws. Add a fallback.Apply these diffs:
- context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + context.client.setQueryData(['todos'], (old) => [...(old ?? []), optimisticTodo])- context.client.setQueryData(['todos'], (old) => - old.map((todo) => + context.client.setQueryData(['todos'], (old) => + (old ?? []).map((todo) => todo.id === onMutateResult.optimisticTodo.id ? result : todo, ), )- context.client.setQueryData(['todos'], (old) => - old.filter((todo) => todo.id !== onMutateResult.optimisticTodo.id), + context.client.setQueryData(['todos'], (old) => + (old ?? []).filter((todo) => todo.id !== onMutateResult.optimisticTodo.id), )Also applies to: 240-242
227-229
: Clarify terminology: “onMutateResult” vs “scope” in commentThis comment says “Return scope…”, while the rest of this guide calls the onMutate return value “onMutateResult”. Align to avoid confusion.
Apply this diff:
- // Return scope with the optimistic todo + // Return onMutateResult with the optimistic todopackages/query-core/src/mutation.ts (2)
213-231
: Optional: avoid double “pending” dispatch by incorporating scope upfrontCurrently, you dispatch
pending
(without scope), then potentially dispatchpending
again afteronMutate
resolves. Consider awaitingonMutate
first and then dispatch a singlepending
with bothvariables
andscope
to reduce churn. Keep if immediate “pending” is required for UX.Example shape:
- this.#dispatch({ type: 'pending', variables, isPaused }) - await this.#mutationCache.config.onMutate?.(variables, this as Mutation<unknown, unknown, unknown, unknown>) - const scope = await this.options.onMutate?.(variables, mutationFnContext) - if (scope !== this.state.scope) { - this.#dispatch({ type: 'pending', scope, variables, isPaused }) - } + await this.#mutationCache.config.onMutate?.(variables, this as Mutation<unknown, unknown, unknown, unknown>) + const scope = await this.options.onMutate?.(variables, mutationFnContext) + this.#dispatch({ type: 'pending', scope, variables, isPaused })
178-183
: DRY: factor context creation into a shared helper
MutationFunctionContext
is constructed here and again in MutationObserver. Extract a small helper to keep them in sync.Proposed helper (new file or colocated util):
// packages/query-core/src/context.ts import type { QueryClient } from './queryClient' import type { MutationFunctionContext, MutationOptions } from './types' export function buildMutationFunctionContext( client: QueryClient, options: Pick<MutationOptions<any, any, any, any>, 'meta' | 'mutationKey'>, ): MutationFunctionContext { return { client, meta: options.meta, mutationKey: options.mutationKey } }Apply this diff here:
- const mutationFnContext = { - client: this.#client, - meta: this.options.meta, - mutationKey: this.options.mutationKey, - } satisfies MutationFunctionContext + const mutationFnContext = buildMutationFunctionContext(this.#client, this.options)And use the same helper in MutationObserver (see comment there).
Also applies to: 242-247
packages/query-core/src/mutationObserver.ts (1)
145-157
: DRY: reuse a shared MutationFunctionContext builderSame context object is hand-built here and in Mutation. Prefer a shared helper to avoid drift.
Apply this diff (after adding the helper suggested in mutation.ts review):
- const context = { - client: this.#client, - meta: this.options.meta, - mutationKey: this.options.mutationKey, - } satisfies MutationFunctionContext + const context = buildMutationFunctionContext(this.#client, this.options)packages/query-core/src/mutationCache.ts (2)
195-208
: find typing is fine; consider removing the cast via a type guard (optional).If
matchMutation
exposed a type predicate, theas Mutation<...>
cast could go away, improving inference. Optional—current approach is acceptable.
234-235
: Guard against empty scope ids to avoid accidental global scoping (optional).Treating
''
as a valid scope key can unintentionally serialize unrelated mutations under the empty id.Apply this small guard:
-function scopeFor(mutation: Mutation<any, any, any, any>) { - return mutation.options.scope?.id -} +function scopeFor(mutation: Mutation<any, any, any, any>) { + const id = mutation.options.scope?.id + return typeof id === 'string' && id.length > 0 ? id : undefined +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (51)
docs/framework/angular/guides/mutations.md
(4 hunks)docs/framework/angular/guides/optimistic-updates.md
(3 hunks)docs/framework/angular/reference/functions/injectmutation.md
(3 hunks)docs/framework/angular/reference/functions/mutationoptions.md
(2 hunks)docs/framework/angular/reference/interfaces/basemutationnarrowing.md
(6 hunks)docs/framework/angular/reference/interfaces/createmutationoptions.md
(2 hunks)docs/framework/angular/reference/type-aliases/createbasemutationresult.md
(3 hunks)docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutatefunction.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutationresult.md
(2 hunks)docs/framework/react/guides/mutations.md
(4 hunks)docs/framework/react/guides/optimistic-updates.md
(3 hunks)docs/framework/react/reference/useMutation.md
(3 hunks)docs/framework/svelte/reference/functions/createmutation.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createbasemutationresult.md
(3 hunks)docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutatefunction.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutationoptions.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutationresult.md
(2 hunks)docs/reference/MutationCache.md
(2 hunks)examples/react/optimistic-updates-cache/src/pages/index.tsx
(3 hunks)packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts
(2 hunks)packages/angular-query-experimental/src/inject-mutation.ts
(5 hunks)packages/angular-query-experimental/src/mutation-options.ts
(2 hunks)packages/angular-query-experimental/src/types.ts
(7 hunks)packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts
(2 hunks)packages/query-core/src/__tests__/hydration.test.tsx
(1 hunks)packages/query-core/src/__tests__/mutationCache.test.tsx
(7 hunks)packages/query-core/src/__tests__/mutationObserver.test.tsx
(2 hunks)packages/query-core/src/__tests__/mutations.test.tsx
(17 hunks)packages/query-core/src/__tests__/utils.test.tsx
(1 hunks)packages/query-core/src/mutation.ts
(12 hunks)packages/query-core/src/mutationCache.ts
(4 hunks)packages/query-core/src/mutationObserver.ts
(8 hunks)packages/query-core/src/queryClient.ts
(1 hunks)packages/query-core/src/types.ts
(8 hunks)packages/query-core/src/utils.ts
(2 hunks)packages/query-devtools/src/Devtools.tsx
(1 hunks)packages/react-query/src/__tests__/mutationOptions.test-d.tsx
(2 hunks)packages/react-query/src/__tests__/useMutation.test.tsx
(4 hunks)packages/react-query/src/mutationOptions.ts
(1 hunks)packages/react-query/src/types.ts
(1 hunks)packages/react-query/src/useMutation.ts
(2 hunks)packages/solid-query/src/__tests__/useMutation.test.tsx
(4 hunks)packages/solid-query/src/types.ts
(1 hunks)packages/solid-query/src/useMutation.ts
(1 hunks)packages/svelte-query/src/createMutation.ts
(1 hunks)packages/svelte-query/src/types.ts
(1 hunks)packages/vue-query/src/mutationCache.ts
(1 hunks)packages/vue-query/src/queryClient.ts
(1 hunks)packages/vue-query/src/useMutation.ts
(3 hunks)
🔇 Additional comments (65)
packages/solid-query/src/useMutation.ts (2)
15-24
: TScope rename is correctly propagated through the Solid wrapperGenerics, observer instantiation, and exposed types align with query-core. No runtime changes—good.
Also applies to: 26-27, 31-32, 39-40
15-24
: Quick sweep for leftover TContext references in Solid packageSanity-check there are no stale TContext references that could confuse consumers or shadow new types.
#!/bin/bash # Look for any remaining TContext references in Solid package rg -n --type=ts '\bTContext\b' packages/solid-query -C2 # Ensure MutationObserver generics are consistently using 4 params and TScope in Solid rg -n --type=ts 'MutationObserver<[^>]+>' packages/solid-query/src -C2packages/solid-query/src/types.ts (1)
143-159
: Type-level refactor to TScope looks consistent and non-breakingAll mutation-related public types now use TScope; MutateFunction parameter plumbs through correctly; mutate vs mutateAsync split preserved. Looks good.
Also applies to: 160-175, 176-186, 188-194
packages/react-query/src/mutationOptions.ts (1)
8-16
: TScope rename looks correct; overloads remain type-safeAll three overloads consistently replace TContext with TScope. No runtime changes.
Also applies to: 22-29, 33-37
docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md (1)
6-11
: TScope rename LGTMDocs correctly reflect TContext → TScope on the alias and type params.
Also applies to: 21-21
docs/framework/angular/reference/interfaces/createmutationoptions.md (1)
6-11
: TScope rename LGTMExtends clause and type params are consistent.
Also applies to: 20-20
docs/framework/angular/reference/type-aliases/createmutationresult.md (1)
6-12
: TScope rename LGTMSignature and dependent types updated consistently.
Also applies to: 22-25
docs/framework/angular/reference/type-aliases/createbasemutationresult.md (1)
6-16
: TScope rename LGTMOverride target and mutate/mutateAsync types all reflect TScope.
Also applies to: 22-24, 34-34
docs/framework/angular/reference/interfaces/basemutationnarrowing.md (1)
6-6
: TScope rename looks consistent across the interface and nested result types.No functional issues spotted in the doc snippet.
Run to catch any lingering TContext references in Angular docs:
#!/bin/bash rg -n --glob 'docs/framework/angular/**' -S '\bTContext\b|MutationObserver(Result|.*Result)<[^>]*TContext' || trueAlso applies to: 16-16, 28-34, 53-59, 78-84, 103-109
packages/svelte-query/src/types.ts (1)
90-94
: Svelte types: TScope propagation LGTM.
- CreateMutationOptions/Result/Mutate types correctly switch to TScope.
- Async mutate aliasing to core MutateFunction is sound.
Nothing blocking.
Also applies to: 100-103, 106-111, 116-122, 129-131
packages/svelte-query/src/createMutation.ts (1)
17-18
: TScope wiring through createMutation is correct.Observer generic parameters and returned types align with the new TScope signatures.
Also applies to: 19-21, 26-27, 30-31
packages/query-core/src/__tests__/utils.test.tsx (1)
426-431
: Test update to pass client into Mutation constructor is correct.Matches the new constructor contract; assertions read well.
packages/react-query/src/__tests__/useMutation.test.tsx (1)
528-533
: Callback “context arg” assertions are consistent and correct.
- onMutate now receives (variables, { client, meta, mutationKey }).
- onSuccess/onError/onSettled include the 4th descriptor param consistently.
Looks good.
Also applies to: 986-991, 998-1002, 1010-1014, 1022-1026, 1060-1064, 1142-1146
packages/query-core/src/__tests__/hydration.test.tsx (1)
544-549
: Hydration: new 4th callback param assertion matches the API.The descriptor object with client/meta/mutationKey is validated correctly.
packages/query-core/src/__tests__/mutationCache.test.tsx (1)
30-31
: “scope” terminology aligned across mutationCache tests.Return value from onMutate and the 3rd arg to callbacks updated coherently.
Also applies to: 42-43, 51-53, 112-113, 124-126, 190-191
packages/vue-query/src/mutationCache.ts (1)
15-19
: Rename to TScope in generics looks good.Consistent with core’s Mutation/MutationFilters generics and keeps Vue wrapper aligned.
packages/query-core/src/utils.ts (2)
46-52
: TScope rename in MutationFilters is correct.Type-only change; no runtime impact.
59-60
: Predicate now typed with TScope—good.Keeps user predicates accurately typed against the scoped Mutation instance.
docs/framework/svelte/reference/type-aliases/createmutationresult.md (2)
6-12
: Docs: TScope rename reads well.Header and snippet correctly reflect
TScope
.
28-29
: Verify “Defined in” anchors after merge.Line numbers often shift; ensure the link still points to the typedef.
#!/bin/bash # Confirm typedef location for CreateMutationResult rg -n "^export type CreateMutationResult<" packages/svelte-query/src/types.tspackages/query-core/src/queryClient.ts (1)
511-522
: setMutationDefaults: TScope migration looks correct.Signature and options typing now align with the new generic parameter.
docs/framework/svelte/reference/type-aliases/createmutationoptions.md (2)
6-13
: Docs: TScope update matches core types.Type alias and snippet are consistent with MutationObserverOptions’s new generic parameter.
27-29
: Verify “Defined in” anchor.Ensure the link remains accurate post-merge.
#!/bin/bash rg -n "^export type CreateMutationOptions<" packages/svelte-query/src/types.tsdocs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md (2)
6-11
: Docs: async mutate function now parameterized by TScope—looks good.Keeps MutateFunction wiring consistent with the rest of the TScope changes.
23-26
: Check “Defined in” anchor.Same note on potential line drift.
#!/bin/bash rg -n "^export type CreateMutateAsyncFunction<" packages/svelte-query/src/types.tspackages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (3)
11-14
: Importing MutationFunctionContext is appropriate.Aligns Angular tests with the new context argument.
62-75
: onMutateResult flow is validated correctly.Good check that
onSuccess
receives{ name: string } | undefined
.
77-95
: Context threaded to mutateFn and callbacks—nice coverage.Covers mutateFn, onMutate, onSuccess, onError, and onSettled parameter positions and types.
packages/vue-query/src/queryClient.ts (1)
459-465
: Types rename LGTM (TContext → TScope) for setMutationDefaultsSignature and MutationObserverOptions generics are updated consistently. No runtime impact.
docs/framework/angular/reference/functions/injectmutation.md (1)
9-13
: Signature rename to TScope is consistent with implementationDocs reflect the API change accurately.
packages/angular-query-experimental/src/inject-mutation.ts (1)
45-59
: TScope migration looks correctGeneric rename and type wiring (MutationObserver, CreateMutateFunction/Result) are consistent. No behavioral regressions spotted.
Also applies to: 72-83, 101-107, 166-172
docs/reference/MutationCache.md (1)
31-41
: Callbacks parameter rename to scope reads wellThe onError/onSuccess/onSettled signatures now use scope; matches PR intent.
docs/framework/svelte/reference/type-aliases/createmutatefunction.md (1)
6-10
: TScope rename in Svelte type alias is consistentDocs align with types; DefaultError default is correct.
Also applies to: 20-27
packages/query-core/src/__tests__/mutationObserver.test.tsx (1)
320-339
: Tests cover new options argument and callback orderGreat assertions for the extra options object and argument order in success/error paths.
Also applies to: 365-383
packages/react-query/src/useMutation.ts (1)
23-27
: TScope generic is correctly plumbed through useMutation. LGTM.Type-only changes look consistent across options, observer, and mutate types; runtime remains unchanged.
Also applies to: 32-33, 50-51
docs/framework/angular/reference/functions/mutationoptions.md (1)
9-12
: Mechanical rename to TScope is accurate.Signatures and return types are updated consistently.
Also applies to: 55-56, 61-62
packages/angular-query-experimental/src/mutation-options.ts (1)
38-47
: TScope rename across overloads/impl looks correct.Overloads remain source-compatible; implementation stays runtime no-op.
Also applies to: 52-59, 94-98
docs/framework/svelte/reference/type-aliases/createbasemutationresult.md (3)
6-6
: Rename to TScope looks goodPublic docs now align with the API direction (context → scope).
9-13
: Propagate TScope through nested typesGood consistency across MutationObserverResult and mutate signatures.
23-24
: mutateAsync generic alignment is correctMatches the mutate signature and new fourth generic.
packages/react-query/src/types.ts (5)
202-210
: UseMutateFunction mirrors core MutateFunction paramsUsing Parameters<> preserves the parameter list while fixing return type to void for mutate. Good.
211-217
: UseMutateAsyncFunction alias is consistentAsync variant cleanly aliases core MutateFunction with TScope.
218-227
: UseBaseMutationResult wiring is correctTScope is consistently threaded through result, mutate, and mutateAsync.
228-234
: UseMutationResult type alias updated correctlyAlias now forwards TScope as intended.
191-200
: TScope generic on UseMutationOptionsRename and threading into MutationObserverOptions looks correct. Please ensure 3-generic call-sites still infer as before (fourth defaults to unknown).
You can quickly scan for 3-arg usages to confirm test coverage:
#!/bin/bash rg -nP '\bUseMutationOptions<\s*[^,>]+,\s*[^,>]+,\s*[^,>]+\s*>' -g '!**/node_modules/**'packages/query-core/src/__tests__/mutations.test.tsx (2)
779-781
: 4th callback arg renamed to scope — correct usageMatches the new contract; assertion string is consistent.
831-833
: Error-path onSettled receives scope — correctCovers the de-duped callback path with scope data.
docs/framework/react/guides/mutations.md (2)
146-162
: Example “Mutation Side Effects” looks correct with new parameters.Signatures match the new API: onMutate(variables, context) and callbacks receiving (…, onMutateResult, context).
297-340
: Optimistic update flow with context.client looks good.The usage of context.client for cancelQueries/setQueryData and passing onMutateResult through callbacks is consistent with the new contract.
packages/vue-query/src/useMutation.ts (1)
27-34
: TScope adoption and wiring to core generics look correct.Types line up with core’s <TData, TError, TVariables, TOnMutateResult>, and the wrapper correctly exposes mutate (void) and mutateAsync (Promise).
Also applies to: 32-34, 39-41, 46-49, 55-61, 67-74, 95-99, 117-119
packages/query-core/src/types.ts (3)
1094-1099
: Introduce MutationFunctionContext: good shape and minimal surface.client, mutationKey, meta cover the typical needs; consistent with docs.
Also applies to: 1101-1104
1105-1117
: Lifecycle signatures look consistent and safe (onMutateResult is unioned with undefined).Changing to TOnMutateResult | undefined removes the previous unsound non-null assertions.
Also applies to: 1118-1143
1154-1179
: Confirm downstream wrappers align with Promise-returning mutate.Core exposes mutate as Promise-returning (for wrappers to map to mutateAsync). React/Vue/Svelte wrappers should keep exposing mutate void and pipe state.mutate to mutateAsync. Looks already correct for Vue in this PR; please ensure others match.
Would you like a quick scan script to spot any wrapper that still re-exports Promise-returning mutate?
Also applies to: 1181-1189, 1249-1250
packages/query-core/src/mutation.ts (2)
178-183
: Good: well-typed MutationFunctionContext constructionUsing
satisfies MutationFunctionContext
ensures structural conformance without widening types.
32-37
: Deprecation note looks goodClear guidance and timeline for
context
→scope
migration in state.packages/query-core/src/mutationObserver.ts (1)
17-32
: API surface rename to TScope: looks consistentGeneric renames, result typing, and listener signatures align with the new “scope” concept.
Also applies to: 88-101, 129-143
packages/query-core/src/mutationCache.ts (3)
13-37
: Rename to “scope” looks good—please confirm cross-API consistency and docs alignment.Signatures now pass
scope
to onError/onSuccess/onSettled. Double-check:
- All adapters (React/Vue/Svelte/Angular/Solid) forward
scope
consistently to their public callbacks.- Docs/examples reference
scope
(and, where applicable,onMutateResult
) and call out deprecation ofcontext
.You can quickly scan for lingering “context” in mutation callback parameters:
#!/bin/bash rg -nP --glob='packages/**/src/**' '\bon(Error|Success|Settled)\s*\([^)]*\bcontext\b' rg -nP --glob='docs/**' '\bcontext\b' -C2
96-103
: Threading QueryClient into Mutation is sensible. Verify Mutation’s config type includes it.Passing
client
intonew Mutation({...})
assumesMutationConfig
acceptsclient: QueryClient
. Please confirm the constructor and type are updated accordingly to avoid structural typing pitfalls.#!/bin/bash # Show Mutation constructor/config shape and confirm 'client' is present: rg -nP 'class\s+Mutation\b' packages/query-core/src -n -C3 rg -nP 'interface\s+MutationConfig|type\s+MutationConfig' packages/query-core/src -n -C3 rg -nP 'constructor\s*\(' packages/query-core/src/mutation.ts -n -C3
49-58
: Potential generic arity drift for MutationObserver.These notify types use
MutationObserver<any, any, any>
. IfMutationObserver
now has<TData, TError, TVariables, TScope>
, this will compile-error. If it’s still 3 params, ignore.#!/bin/bash # Verify MutationObserver generic arity and usages: rg -nP '^export\s+(?:class|interface|type)\s+MutationObserver\s*<([^>]*)>' packages/query-core/src -C1 rg -nP '\bMutationObserver<[^>]*>' packages/query-core/src -npackages/angular-query-experimental/src/types.ts (6)
162-171
: TScope migration for CreateMutationOptions aligns with core.The generic rename and downstream mapping to
MutationObserverOptions<..., TScope>
look correct.
197-207
: CreateBaseMutationResult override is consistent with TScope propagation.Override of
mutate
/mutateAsync
with TScope threading looks good.
212-221
: Status-narrowed mutation result updates are coherent.
CreateStatusBasedMutationResult<..., TScope>
wiring matches the base result changes.
228-296
: Type guards preserve TScope across narrows.
this: CreateMutationResult<..., TScope>
ensures scope sticks throughisSuccess/isError/isPending/isIdle
. Looks correct.
301-315
: Top-level CreateMutationResult threads TScope end-to-end.No issues spotted with the
TState
default andMapToSignals
projection.
175-193
: Default TVariables differs across aliases—confirm this is intentional.Here,
CreateMutateFunction/CreateMutateAsyncFunction
useTVariables = void
, whileCreateBaseMutationResult
below defaultsTVariables = unknown
. Consider aligning defaults or documenting why they differ to prevent surprising inference.#!/bin/bash # Check defaults for TVariables across Angular types: rg -nP 'TVariables\s*=\s*(void|unknown)' packages/angular-query-experimental/src/types.ts -n # Compare with core: rg -nP 'type\s+MutateFunction<|interface\s+MutationObserverResult<' packages/query-core/src -n -C2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still several places where scope
is referenced, please have a look at them 🙏
cb89bfe
to
5deb6f4
Compare
* @deprecated `context` was renamed to `scope`. This will be removed in the next major version. Use the `scope` property instead. | ||
*/ | ||
context: TOnMutateResult | undefined | ||
scope: TOnMutateResult | undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TkDodo should we rename this variable then? or do you think this is fine, this is the only hangover of scope
left afaik
5deb6f4
to
60d0300
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/svelte-query/src/createMutation.ts (1)
36-41
: Fix leaked subscriptions in query/mutation creators
- packages/svelte-query/src/createMutation.ts (L36–41): move
optionsStore.subscribe
into thereadable
setup callback and unsubscribe on teardown- optionsStore.subscribe(($options) => { - mutate = (variables, mutateOptions) => { - observer.mutate(variables, mutateOptions).catch(noop) - } - observer.setOptions($options) - }) - - const result = readable(observer.getCurrentResult(), (set) => { - return observer.subscribe(notifyManager.batchCalls((val) => set(val))) - }) + const result = readable(observer.getCurrentResult(), (set) => { + // Subscribe to options with the store lifecycle to avoid leaks + const unsubscribeOptions = optionsStore.subscribe(($options) => { + mutate = (variables, mutateOptions) => { + observer.mutate(variables, mutateOptions).catch(noop) + } + observer.setOptions($options) + }) + const unsubscribeObserver = observer.subscribe( + notifyManager.batchCalls((val) => set(val)), + ) + return () => { + unsubscribeObserver() + unsubscribeOptions?.() + } + })
- packages/svelte-query/src/createBaseQuery.ts (L58–60): wrap
defaultedOptionsStore.subscribe
inside thereadable
teardown pattern and clean up- packages/svelte-query/src/createQueries.ts (L232–234): wrap
defaultedQueriesStore.subscribe
inside thereadable
setup/cleanupdocs/framework/angular/reference/functions/injectmutation.md (1)
21-28
: Fix default type for TError (DefaultError vs Error)Docs show TError = Error, but implementation uses DefaultError. Mismatch can confuse users.
Apply:
-• **TError** = `Error` +• **TError** = `DefaultError`docs/framework/angular/guides/mutations.md (1)
251-253
: Use this.mutation inside class method
mutation
is not in scope; should reference the class property.- mutation.mutate({ title: 'title' }) + this.mutation.mutate({ title: 'title' })packages/query-core/src/mutationCache.ts (1)
58-67
: Type mismatch: MutationObserver generics inconsistent (3 vs 4)
MutationObserver
now has 4 generics; two places still use 3.interface NotifyEventMutationObserverAdded extends NotifyEvent { type: 'observerAdded' mutation: Mutation<any, any, any, any> - observer: MutationObserver<any, any, any> + observer: MutationObserver<any, any, any, any> } @@ interface NotifyEventMutationObserverRemoved extends NotifyEvent { type: 'observerRemoved' mutation: Mutation<any, any, any, any> - observer: MutationObserver<any, any, any> + observer: MutationObserver<any, any, any, any> }Also applies to: 69-73
♻️ Duplicate comments (8)
docs/framework/svelte/reference/functions/createmutation.md (1)
19-19
: Fix default TError in docs (DefaultError, not Error)Docs should mirror implementation defaults.
Apply:
-• **TError** = `Error` +• **TError** = `DefaultError`docs/framework/angular/guides/optimistic-updates.md (3)
90-104
: Guard setQueryData updater against undefined oldold can be undefined; spread will throw. Use nullish coalescing.
- context.client.setQueryData(['todos'], (old) => [...old, newTodo]) + context.client.setQueryData(['todos'], (old) => [ ...(old ?? []), newTodo ])
147-149
: Return/await invalidation for per-id example- onSettled: (newTodo, error, variables, onMutateResult, context) => { - context.client.invalidateQueries({ queryKey: ['todos', newTodo.id] }) - }, + onSettled: async (newTodo, _e, _v, _r, context) => { + return await context.client.invalidateQueries({ queryKey: ['todos', newTodo.id] }) + },
110-112
: Return/await invalidation to keep mutation pending until refetch finishesMake onSettled async and return the Promise.
- onSettled: (data, error, variables, onMutateResult, context) => { - context.client.invalidateQueries({ queryKey: ['todos'] }) - }, + onSettled: async (_d, _e, _v, _r, context) => { + return await context.client.invalidateQueries({ queryKey: ['todos'] }) + },docs/reference/MutationCache.md (1)
53-54
: Clarify that global onMutate return is ignoredCurrent text is still ambiguous. Suggest explicit wording to avoid implying propagation.
-– `onMutate` does not allow returning a result. +– The return value of `onMutate` on the global MutationCache is ignored and not propagated (no onMutateResult is passed on).docs/framework/react/guides/mutations.md (2)
190-214
: Fix undefined identifier: destructure mutate from useMutation
mutate(...)
is used without being declared.-useMutation({ +const { mutate } = useMutation({ mutationFn: addTodo, onSuccess: (data, variables, onMutateResult, context) => { // I will fire first }, onError: (error, variables, onMutateResult, context) => { // I will fire first }, onSettled: (data, error, variables, onMutateResult, context) => { // I will fire first }, }) mutate(todo, {
227-243
: Same issue in “Consecutive mutations” exampleDeclare
mutate
before using it.-useMutation({ +const { mutate } = useMutation({ mutationFn: addTodo, onSuccess: (data, variables, onMutateResult, context) => { // Will be called 3 times }, }) const todos = ['Todo 1', 'Todo 2', 'Todo 3'] todos.forEach((todo) => { mutate(todo, {docs/framework/react/reference/useMutation.md (1)
67-81
: Fix onMutate signature and clarify propagation targetDocs omit the
context
param and understate propagation.-- `onMutate: (variables: TVariables) => Promise<TOnMutateResult | void> | TOnMutateResult | void` +- `onMutate: (variables: TVariables, context: MutationFunctionContext) => Promise<TOnMutateResult | void> | TOnMutateResult | void` @@ - - The value returned from this function will be passed to both the `onError` and `onSettled` functions in the event of a mutation failure and can be useful for rolling back optimistic updates. + - The value returned from this function will be passed to `onSuccess`, `onError`, and `onSettled`. This enables optimistic updates and rollbacks.
🧹 Nitpick comments (10)
packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts (1)
146-146
: Undefined identifier in onError fixture: use onMutateResult instead of scope.The logged value should reference the callback param. Using scope is inconsistent with the new API and would be undefined if executed.
Apply this diff:
-return 'onError: (error, variables, onMutateResult) => {\n console.log("error:", error, "onMutateResult:", scope)\n}' +return 'onError: (error, variables, onMutateResult) => {\n console.log("error:", error, "onMutateResult:", onMutateResult)\n}'packages/solid-query/src/useMutation.ts (1)
42-44
: State type updated; minor init nit: avoid double getCurrentResult().Micro-alloc reduction and readability.
Apply:
- const [state, setState] = createStore< - UseMutationResult<TData, TError, TVariables, TOnMutateResult> - >({ - ...observer.getCurrentResult(), - mutate, - mutateAsync: observer.getCurrentResult().mutate, - }) + const initialResult = observer.getCurrentResult() + const [state, setState] = createStore< + UseMutationResult<TData, TError, TVariables, TOnMutateResult> + >({ + ...initialResult, + mutate, + mutateAsync: initialResult.mutate, + })packages/solid-query/src/types.ts (1)
147-151
: Add JSDoc for TOnMutateResult on SolidMutationOptions
Document that theTOnMutateResult
type parameter represents the return value of theonMutate
callback.packages/svelte-query/src/types.ts (1)
96-105
: Minor: tighten readability by naming the parameters type.Using
Parameters<...>
inline is fine, but a local alias improves readability and compiler diagnostics.export type CreateMutateFunction< TData = unknown, TError = DefaultError, TVariables = void, TOnMutateResult = unknown, > = ( - ...args: Parameters< - MutateFunction<TData, TError, TVariables, TOnMutateResult> - > + ...args: MutateArgs<TData, TError, TVariables, TOnMutateResult> ) => void + +export type MutateArgs< + TData = unknown, + TError = DefaultError, + TVariables = void, + TOnMutateResult = unknown, +> = Parameters<MutateFunction<TData, TError, TVariables, TOnMutateResult>>packages/vue-query/src/useMutation.ts (1)
61-72
: Avoid exposing/overwriting mutate/reset via toRefs; omit them before toRefsCurrently toRefs(readonlyState) includes mutate/reset, which you then overwrite. Cleanly omit first to match MutationResult and drop the unsafe cast.
Apply:
- const resultRefs = toRefs(readonlyState) as ToRefs< - Readonly<MutationResult<TData, TError, TVariables, TOnMutateResult>> - > + const { mutate: _m, reset: _r, ...rest } = readonlyState as any + const resultRefs = toRefs(rest) as ToRefs< + Readonly<MutationResult<TData, TError, TVariables, TOnMutateResult>> + >Also applies to: 127-131
packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (1)
117-142
: Optional: add a test where onMutate returns void to assert onMutateResult is undefinedCovers the negative path and mirrors React/Svelte suites.
Proposed addition:
it('onMutate returning void yields undefined onMutateResult', () => { mutationOptions<number>({ mutationFn: (id: string) => Promise.resolve(id.length), onMutate: () => {}, onSuccess: (_d, _v, onMutateResult) => { expectTypeOf(onMutateResult).toEqualTypeOf<undefined>() }, }) })docs/framework/angular/guides/optimistic-updates.md (1)
65-71
: Optional: mirror the earlier pending-state patternFor consistency with the first snippet, consider returning the Promise in this earlier onSettled too.
- onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),(If you want, make it async + return as in the other examples.)
packages/query-core/src/__tests__/mutations.test.tsx (1)
796-796
: Remove stray console.log in testsAvoids noisy test output.
- console.log(results)
packages/query-core/src/mutationObserver.ts (2)
166-166
: Rename local “scope” to “onMutateResult” for clarity with MutateOptions signature.Internally we store the value as scope, but MutateOptions’ 3rd parameter is named onMutateResult. Renaming here reduces cognitive overhead.
- const scope = this.#currentResult.scope + const onMutateResult = this.#currentResult.scope @@ - this.#mutateOptions.onSuccess?.( - action.data, - variables, - scope, - context, - ) + this.#mutateOptions.onSuccess?.( + action.data, + variables, + onMutateResult, + context, + ) @@ - this.#mutateOptions.onSettled?.( + this.#mutateOptions.onSettled?.( action.data, null, variables, - scope, + onMutateResult, context, ) @@ - this.#mutateOptions.onError?.(action.error, variables, scope, context) + this.#mutateOptions.onError?.( + action.error, + variables, + onMutateResult, + context, + ) @@ this.#mutateOptions.onSettled?.( undefined, action.error, variables, - scope, + onMutateResult, context, )Also applies to: 175-187, 189-195
164-165
: Consider invoking mutateOptions callbacks regardless of subscription presence.Current guard requires hasListeners(); this can suppress onSuccess/onError/onSettled when using the observer imperatively without subscribers.
If this wasn’t intentional, change:
- if (this.#mutateOptions && this.hasListeners()) { + if (this.#mutateOptions) {Would you like a follow-up PR to add a test that asserts callbacks fire with zero listeners?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (45)
docs/framework/angular/guides/mutations.md
(4 hunks)docs/framework/angular/guides/optimistic-updates.md
(3 hunks)docs/framework/angular/reference/functions/injectmutation.md
(3 hunks)docs/framework/angular/reference/functions/mutationoptions.md
(2 hunks)docs/framework/angular/reference/interfaces/basemutationnarrowing.md
(6 hunks)docs/framework/angular/reference/interfaces/createmutationoptions.md
(2 hunks)docs/framework/angular/reference/type-aliases/createbasemutationresult.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutatefunction.md
(2 hunks)docs/framework/angular/reference/type-aliases/createmutationresult.md
(2 hunks)docs/framework/react/guides/mutations.md
(4 hunks)docs/framework/react/guides/optimistic-updates.md
(3 hunks)docs/framework/react/reference/useMutation.md
(3 hunks)docs/framework/svelte/reference/functions/createmutation.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createbasemutationresult.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutatefunction.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutationoptions.md
(2 hunks)docs/framework/svelte/reference/type-aliases/createmutationresult.md
(2 hunks)docs/reference/MutationCache.md
(2 hunks)examples/react/optimistic-updates-cache/src/pages/index.tsx
(2 hunks)packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts
(2 hunks)packages/angular-query-experimental/src/inject-mutation.ts
(4 hunks)packages/angular-query-experimental/src/mutation-options.ts
(2 hunks)packages/angular-query-experimental/src/types.ts
(7 hunks)packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts
(2 hunks)packages/query-core/src/__tests__/mutationCache.test.tsx
(5 hunks)packages/query-core/src/__tests__/mutations.test.tsx
(17 hunks)packages/query-core/src/mutation.ts
(12 hunks)packages/query-core/src/mutationCache.ts
(4 hunks)packages/query-core/src/mutationObserver.ts
(8 hunks)packages/query-core/src/queryClient.ts
(1 hunks)packages/query-core/src/types.ts
(8 hunks)packages/query-core/src/utils.ts
(2 hunks)packages/react-query/src/__tests__/mutationOptions.test-d.tsx
(2 hunks)packages/react-query/src/mutationOptions.ts
(1 hunks)packages/react-query/src/types.ts
(1 hunks)packages/react-query/src/useMutation.ts
(2 hunks)packages/solid-query/src/types.ts
(1 hunks)packages/solid-query/src/useMutation.ts
(1 hunks)packages/svelte-query/src/createMutation.ts
(1 hunks)packages/svelte-query/src/types.ts
(1 hunks)packages/vue-query/src/mutationCache.ts
(1 hunks)packages/vue-query/src/queryClient.ts
(1 hunks)packages/vue-query/src/useMutation.ts
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
- docs/framework/angular/reference/functions/mutationoptions.md
- docs/framework/angular/reference/type-aliases/createmutateasyncfunction.md
- docs/framework/react/guides/optimistic-updates.md
- docs/framework/svelte/reference/type-aliases/createbasemutationresult.md
- packages/react-query/src/mutationOptions.ts
- docs/framework/angular/reference/interfaces/createmutationoptions.md
- packages/vue-query/src/mutationCache.ts
- packages/query-core/src/utils.ts
- packages/query-core/src/queryClient.ts
- packages/query-core/src/tests/mutationCache.test.tsx
- docs/framework/svelte/reference/type-aliases/createmutationoptions.md
- packages/react-query/src/useMutation.ts
- docs/framework/angular/reference/type-aliases/createmutationresult.md
- examples/react/optimistic-updates-cache/src/pages/index.tsx
- docs/framework/svelte/reference/type-aliases/createmutateasyncfunction.md
- packages/query-core/src/mutation.ts
- docs/framework/angular/reference/type-aliases/createmutatefunction.md
- packages/query-core/src/types.ts
🧰 Additional context used
🧬 Code graph analysis (13)
packages/svelte-query/src/types.ts (2)
packages/query-core/src/types.ts (6)
OmitKeyof
(19-29)MutationObserverOptions
(1145-1152)DefaultError
(47-51)MutateFunction
(1181-1189)Override
(31-35)MutationObserverResult
(1340-1349)packages/angular-query-experimental/src/types.ts (4)
CreateMutateFunction
(175-184)CreateMutateAsyncFunction
(189-194)CreateBaseMutationResult
(199-214)CreateMutationResult
(314-327)
packages/vue-query/src/queryClient.ts (2)
packages/vue-query/src/types.ts (1)
MaybeRefDeep
(29-37)packages/query-core/src/types.ts (1)
MutationObserverOptions
(1145-1152)
packages/svelte-query/src/createMutation.ts (3)
packages/svelte-query/src/types.ts (4)
StoreOrVal
(21-21)CreateMutationOptions
(86-94)CreateMutationResult
(132-139)CreateMutateFunction
(96-105)packages/query-core/src/queryClient.ts (1)
QueryClient
(61-648)packages/query-core/src/mutationObserver.ts (2)
MutationObserver
(23-206)mutate
(128-143)
packages/solid-query/src/useMutation.ts (3)
packages/react-query/src/types.ts (3)
UseMutationOptions
(192-200)UseMutationResult
(237-242)UseMutateFunction
(202-211)packages/solid-query/src/types.ts (3)
UseMutationOptions
(153-158)UseMutationResult
(195-200)UseMutateFunction
(160-169)packages/query-core/src/mutationObserver.ts (3)
MutationObserver
(23-206)mutate
(128-143)state
(145-159)
packages/react-query/src/types.ts (2)
packages/query-core/src/types.ts (5)
MutationObserverOptions
(1145-1152)DefaultError
(47-51)MutateFunction
(1181-1189)Override
(31-35)MutationObserverResult
(1340-1349)packages/solid-query/src/types.ts (2)
UseMutateFunction
(160-169)UseMutateAsyncFunction
(171-176)
packages/query-core/src/mutationCache.ts (3)
packages/query-core/src/mutation.ts (2)
Mutation
(88-389)MutationState
(26-45)packages/query-core/src/types.ts (3)
MutationFunctionContext
(1094-1098)DefaultError
(47-51)MutationOptions
(1105-1143)packages/query-core/src/utils.ts (2)
MutationFilters
(46-70)matchMutation
(175-202)
packages/angular-query-experimental/src/inject-mutation.ts (2)
packages/angular-query-experimental/src/types.ts (3)
CreateMutationOptions
(162-170)CreateMutationResult
(314-327)CreateMutateFunction
(175-184)packages/query-core/src/mutationObserver.ts (1)
MutationObserver
(23-206)
packages/angular-query-experimental/src/types.ts (2)
packages/query-core/src/types.ts (5)
OmitKeyof
(19-29)MutationObserverOptions
(1145-1152)MutateFunction
(1181-1189)Override
(31-35)MutationObserverResult
(1340-1349)packages/svelte-query/src/types.ts (4)
CreateMutateFunction
(96-105)CreateMutateAsyncFunction
(107-112)CreateBaseMutationResult
(114-129)CreateMutationResult
(132-139)
packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (2)
packages/react-query/src/mutationOptions.ts (1)
mutationOptions
(32-41)packages/query-core/src/types.ts (2)
DefaultError
(47-51)MutationFunctionContext
(1094-1098)
packages/query-core/src/mutationObserver.ts (3)
packages/query-core/src/types.ts (4)
MutationObserverResult
(1340-1349)MutationObserverOptions
(1145-1152)MutateOptions
(1154-1179)MutationFunctionContext
(1094-1098)packages/query-core/src/subscribable.ts (1)
Subscribable
(1-30)packages/query-core/src/mutation.ts (4)
Mutation
(88-389)action
(319-388)Action
(78-84)getDefaultState
(391-409)
packages/solid-query/src/types.ts (3)
packages/query-core/src/types.ts (6)
OmitKeyof
(19-29)MutationObserverOptions
(1145-1152)DefaultError
(47-51)MutateFunction
(1181-1189)Override
(31-35)MutationObserverResult
(1340-1349)packages/react-query/src/types.ts (5)
UseMutationOptions
(192-200)UseMutateFunction
(202-211)UseMutateAsyncFunction
(213-218)UseBaseMutationResult
(220-235)UseMutationResult
(237-242)packages/vue-query/src/useMutation.ts (1)
UseMutationOptions
(37-44)
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (2)
packages/react-query/src/mutationOptions.ts (1)
mutationOptions
(32-41)packages/query-core/src/types.ts (2)
DefaultError
(47-51)MutationFunctionContext
(1094-1098)
packages/vue-query/src/useMutation.ts (3)
packages/query-core/src/types.ts (6)
DistributiveOmit
(14-17)MutationObserverResult
(1340-1349)MutationObserverOptions
(1145-1152)DefaultError
(47-51)MutateFunction
(1181-1189)MutateOptions
(1154-1179)packages/vue-query/src/types.ts (2)
ShallowOption
(58-63)MaybeRefDeep
(29-37)packages/react-query/src/useMutation.ts (1)
useMutation
(19-69)
🔇 Additional comments (47)
packages/eslint-plugin-query/src/__tests__/mutation-property-order.rule.test.ts (2)
148-149
: LGTM: onSettled signature and logging align with rename.
205-210
: LGTM: regression test updated to onMutateResult consistently.packages/solid-query/src/useMutation.ts (3)
19-24
: Rename to TOnMutateResult is consistent and non-breaking for consumers.Types align with query-core and other bindings. Good change.
33-39
: Mutate function typing mirrors core signature via Parameters<>.Keeps mutate’s arg types in sync with core. Looks good.
26-31
: Approve: Observer generics are correctly wired and no leftoverTContext
references remain.packages/solid-query/src/types.ts (5)
153-159
: UseMutationOptions updated to Accessor<SolidMutationOptions<…, TOnMutateResult>>.Matches Solid’s Accessor pattern; OK.
160-169
: UseMutateFunction switched to MutateFunction<…, TOnMutateResult> via Parameters<>.Keeps callsite types aligned with core; good.
171-177
: UseMutateAsyncFunction alias updated.Promise-returning mutateAsync correctly mirrors core’s MutateFunction.
178-193
: UseBaseMutationResult override and extras (mutate/mutateAsync) updated.Override shape matches React/Vue counterparts; consistent.
195-201
: UseMutationResult passthrough updated.End-to-end generic flow is consistent.
packages/svelte-query/src/types.ts (4)
107-113
: Async mutate alias aligns with core.
CreateMutateAsyncFunction
correctly mirrorsMutateFunction
withTOnMutateResult
. LGTM.
114-129
: Result typing override is consistent.Overriding
mutate
to the void-returning version and exposingmutateAsync
separately matches existing framework conventions. LGTM.
131-139
: Readable result threading TOnMutateResult is correct.The store wraps
CreateBaseMutationResult<..., TOnMutateResult>
as expected. LGTM.
85-94
: Approve TOnMutateResult rename; no leftover TContext references
All uses of “context” now refer solely to Svelte’s context API, and noTContext
type remains.packages/svelte-query/src/createMutation.ts (3)
17-24
: Generic rename is correctly threaded through the public API.
createMutation
now exposesTOnMutateResult
across options and result. LGTM.
28-35
: Observer instantiation and local mutate type propagation are consistent.Generics on
MutationObserver
andCreateMutateFunction
match core. LGTM.
47-51
: AliasmutateAsync
from core result: OK.Mapping
mutateAsync: $result.mutate
preserves the Promise-returning API whilemutate
stays fire-and-forget. LGTM.packages/vue-query/src/queryClient.ts (1)
459-465
: Type rename to TOnMutateResult in setMutationDefaults — LGTMSignature aligns with core MutationObserverOptions generics; no runtime impact.
docs/framework/svelte/reference/type-aliases/createmutatefunction.md (1)
6-10
: Rename to TOnMutateResult — LGTMDocs and signature are consistent with the updated generics across the codebase.
Also applies to: 22-29
docs/framework/svelte/reference/type-aliases/createmutationresult.md (1)
6-11
: Rename to TOnMutateResult — LGTMMatches CreateBaseMutationResult usage and default type params.
Also applies to: 23-23
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (2)
58-71
: onMutateResult inference path verified — LGTMThe test correctly asserts onMutateResult flow and optionality.
73-91
: Confirm callback argument order matches core types
Verify that theonSuccess
/onError
/onSettled
callbacks here use the exact parameter order and arity defined inMutateOptions
(in packages/query-core/src/types.ts)—the context returned byonMutate
should be the final argument.docs/framework/angular/reference/type-aliases/createbasemutationresult.md (1)
6-17
: Angular docs updated to TOnMutateResult — LGTMAll dependent type references (Override, mutate/mutateAsync) are consistent.
Also applies to: 24-29, 40-40
packages/react-query/src/types.ts (1)
191-243
: React mutation typings migrated to TOnMutateResult — LGTMUseMutationOptions/Result and mutate function aliases correctly propagate the new generic.
docs/framework/svelte/reference/functions/createmutation.md (1)
9-13
: Rename to TOnMutateResult — LGTMSignature, options, and return types are aligned with the new generic.
Also applies to: 23-23, 29-29, 37-37
docs/framework/angular/reference/interfaces/basemutationnarrowing.md (1)
6-17
: Interface generic rename to TOnMutateResult — LGTMNarrowing helpers and mutate signatures consistently reflect the new generic.
packages/vue-query/src/useMutation.ts (2)
27-35
: Rename to TOnMutateResult looks consistent with core typesThe aliasing to MutationObserverResult with TOnMutateResult aligns with query-core generics.
78-85
: API surface and mutate signature updated correctlyGeneric defaults and MutateOptions wiring match query-core. Runtime behavior remains intact.
Also applies to: 106-113
packages/angular-query-experimental/src/types.ts (3)
162-171
: CreateMutationOptions: generic rename is correct and preserves compatibility with core optionsExtends MutationObserverOptions with TOnMutateResult as in core.
175-185
: Mutate function/async types aligned with core MutateFunctionSignatures and parameter forwarding via Parameters<> are spot on.
Also applies to: 189-195
199-214
: Result and narrowing types propagate TOnMutateResult consistentlyType guards and status-based extraction remain correct with the new generic.
Also applies to: 219-229, 235-309, 314-327
packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts (3)
62-75
: onMutateResult inference test looks goodReturn value flows to onSuccess as expected.
77-96
: Context typing coverage is solidAll callbacks receive MutationFunctionContext and assertions are correct.
144-157
: Downstream type inference check is helpfulKeeps CreateMutationResult generic flow honest across injectMutation.
docs/framework/angular/reference/functions/injectmutation.md (1)
8-13
: Docs reflect TOnMutateResult rename correctlySignature and return type are in sync with code.
Also applies to: 45-46
docs/framework/angular/guides/optimistic-updates.md (2)
160-166
: Keep “context” param name; “scope” refers to onMutateResult on stateContext here is the MutationFunctionContext (has client/meta). Do not rename to scope.
If desired, add an example that references state.scope elsewhere to demonstrate the distinction.
125-139
: Guard single-item setQueryData and clarify returned result namingAdd undefined guard; keep comment concise.
- const previousTodo = context.client.getQueryData(['todos', newTodo.id]) + const previousTodo = context.client.getQueryData(['todos', newTodo.id]) @@ - context.client.setQueryData(['todos', newTodo.id], newTodo) + context.client.setQueryData(['todos', newTodo.id], newTodo) @@ - // Return a result with the previous and new todo + // Return onMutateResult with the previous and new todoLikely an incorrect or invalid review comment.
packages/angular-query-experimental/src/inject-mutation.ts (3)
45-59
: Public API generic rename applied correctlyinjectMutation now exposes TOnMutateResult; options and return type mirror that.
72-83
: Observer instance typing updated and stableLocal instance memoization prevents re-instantiation; generics match core.
85-93
: Signal wiring and error propagation LGTMMutate wrapper, options updates, subscription, and re-exposed mutate/mutateAsync align with core behavior.
Also applies to: 105-111, 126-155, 157-168, 170-176
packages/angular-query-experimental/src/mutation-options.ts (2)
38-47
: Type rename to TOnMutateResult looks correctOverload 1 is updated consistently for params and return type. No runtime impact.
55-61
: Consistent TOnMutateResult propagation across overload 2 and implSignatures align with core types; defaults remain intact.
Also applies to: 97-101
docs/reference/MutationCache.md (1)
31-44
: Global callback signatures align with core changesAdded onMutateResult and MutationFunctionContext parameters read correctly.
packages/query-core/src/__tests__/mutations.test.tsx (1)
56-61
: Good: verifies mutationFn receives MutationFunctionContextCovers client, meta, and mutationKey, matching the new signature.
packages/query-core/src/mutationCache.ts (1)
18-46
: Don’t narrow onMutate return to void. onMutate’s return value is passed as the context to onError and onSettled (e.g. for rollbacks); changing it to void | Promise would break that contract.Likely an incorrect or invalid review comment.
packages/query-core/src/mutationObserver.ts (2)
9-12
: Import of MutationFunctionContext is appropriate.Matches its use in #notify; no concerns.
17-19
: TOnMutateResult type plumbed through consistently.Listener, class generics, constructor/setOptions, result getter, mutate signature, and default state are aligned. Good API coherence.
Also applies to: 27-42, 45-51, 66-75, 102-104, 114-115, 130-131, 148-149, 158-159
polite bump @TkDodo – left a comment around |
Description
Kicking off where #9193 left off, i've added the same code modifications to add context to mutationFn, in addition i've changed type references & documentation references to the word
context
in reference to callbacks such asonError | onSuccess | onSettled
to the wordonMutateResult
to avoid confusion –TContext
also becomesTOnMutateResult
.I've not changed the implementation ofDeprecated theMutationObserverResult
fromcontext
toscope
because this would be a breaking change to the APIs available as far as I can tell – maybe this should be a follow up for v6 to create alignment across the codebase?context
value on the mutation state & addedscope
to be primarily used.Finally, I was wondering if we should be passing the context to the aforementioned callbacks but then we do have theAdds the context to the mutation callbacks referenced above.scope
ability to for instance, you could pass the client along as seen in the changed examples...Let me know if i've missed anything or misunderstood some of the agreements in the other PR.
Related Issues
Summary by CodeRabbit
New Features
Documentation