injectMutation
1function injectMutation<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(injectMutationFn: () => CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>, options?: InjectMutationOptions): CreateMutationResult<TData, TError, TVariables, TOnMutateResult>;定义于:packages/angular-query-experimental/src/inject-mutation.ts:172
与查询不同,修改通常用于创建/更新/删除数据或执行服务端副作用。
injectMutation 是用于此目的的函数。与查询不同,修改不会自动运行。
类型参数
TData
TData = unknown
TError
TError = Error
TVariables
TVariables = void
TOnMutateResult
TOnMutateResult = unknown
参数
injectMutationFn
() => CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>
一个返回修改选项的函数。类似于 Angular 中的 computed,该函数在响应式上下文中运行,因此其中读取的信号会驱动修改的选项。
injectMutationFn 属性
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
gcTime? |
number |
undefined |
未使用/非活跃修改在内存中保留的时间(毫秒),超时后将被垃圾回收。默认为 5 * 60 * 1000(5 分钟),SSR 期间为 Infinity。 |
meta? |
Record<string, unknown> |
undefined |
存储在修改缓存条目上的额外负载。可用于传递可在 mutation 可用的任意位置读取的信息,例如 MutationCache 的 onError 和 onSuccess 回调。 |
mutationFn? |
(variables: TVariables, context: MutationFunctionContext) => Promise<TData> |
undefined |
执行该修改所运行的异步任务的函数。必需,除非已通过 queryClient.setMutationDefaults 为匹配的 mutationKey 设置了默认修改函数。接收传入 mutate 的 variables,以及一个包含 QueryClient、mutationKey 和 meta 的 MutationFunctionContext。必须返回一个解析修改数据的 Promise。 |
mutationKey? |
readonly unknown[] |
undefined |
此修改所用的键。可选,但若要继承通过 queryClient.setMutationDefaults 注册的默认值,或要将此修改与 useMutationState 或 queryClient.isMutating 匹配,则必须提供。 |
networkMode? |
"online" | "always" | "offlineFirst" |
'online' |
控制是否允许修改基于当前网络连接状态运行。参见 Network Mode 获取更多信息。 |
onError? |
(error: TError, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => unknown |
undefined |
当修改遇到错误时调用此函数,并传入错误。若返回 Promise,则会在 onSettled 运行前等待其完成。 |
onMutate? |
(variables: TVariables, context: MutationFunctionContext) => TOnMutateResult | Promise<TOnMutateResult> |
undefined |
在修改函数运行前调用此函数,并接收相同的变量。适用于应用乐观更新(期望修改成功)。其返回值将作为 onMutateResult 传给 onSuccess、onError 和 onSettled,乐观更新通常在此处回滚。若返回 Promise,则会在修改函数运行前等待其完成。 |
onSettled? |
(data: TData | undefined, error: TError | null, variables: TVariables, onMutateResult: TOnMutateResult | undefined, context: MutationFunctionContext) => unknown |
undefined |
当修改成功或出错时调用此函数,并传入数据或错误。若返回 Promise,则会在修改最终确定前等待其完成。 |
onSuccess? |
(data: TData, variables: TVariables, onMutateResult: TOnMutateResult, context: MutationFunctionContext) => unknown |
undefined |
当修改成功时调用此函数,并传入修改结果。若返回 Promise,则会在 onSettled 运行前等待其完成。 |
retry? |
| number | false | true | ((failureCount: number, error: TError) => boolean) |
0 |
若为 false,失败的修改默认不重试;若为 true,失败的修改将无限重试;若设为整数(如 3),则失败的修改将重试直至失败次数达到该数值;若设为函数 (failureCount, error) => boolean,则失败的修改将重试直至该函数返回 false。 |
retryDelay? |
number | ((failureCount: number, error: TError) => number) |
undefined |
此函数接收一个 retryAttempt 整数和实际 Error,并返回下次尝试前应延迟的毫秒数。默认为一个应用指数退避(上限为 30 秒)的函数。 |
scope? |
MutationScope |
undefined |
控制此修改是与其他修改并行运行还是等待轮到自己。共享相同 scope.id 的修改将按启动顺序串行运行。若未指定作用域,则修改将在触发后立即运行。 |
throwOnError? |
boolean | ((error: TError) => boolean) |
false |
是否应抛出错误而非设置 error 属性。若设为 true,所有错误都将抛出至最近的错误边界。若设为函数,则会传入错误并应返回布尔值,指示是否抛出错误(true)或将其作为状态返回(false)。 |
options?
其他配置
options 属性
| 属性 | 类型 | 描述 |
|---|---|---|
injector? |
Injector |
创建修改所在的 Injector。若未提供,将改用当前注入上下文(通过 inject)。 |
返回值
CreateMutationResult<TData, TError, TVariables, TOnMutateResult>
修改结果。值字段以 Signal 形式暴露 —— 通过调用它们来读取 data/error(例如 mutation.data())—— 而函数字段(mutate、mutateAsync、reset)则直接调用,保持不变。isSuccess/isError/isPending/isIdle 是类型守卫方法,可用于缩小 data 是否已定义的判断范围。
备注
mutate/mutateAsync 还接受每调用一次的 onSuccess/onError/onSettled 回调作为第二个参数,这有助于在不将调用点副作用(例如导航)耦合到共享修改定义的情况下触发它们。在 injectMutationFn 中定义的回调对每次修改均生效;而每次调用的回调仅对最近一次调用生效 — mutateAsync 为每次调用提供一个 Promise,因此你可以等待 Promise.all/Promise.allSettled 的多次调用并分别查看每次的结果。
参见
mutationOptions 用于在多个 injectMutation 调用点之间共享这些选项,或通过其 mutationKey 在其他位置查找该修改(例如使用 injectMutationState)。
示例
1@Component({2 selector: 'todos',3 template: `4 @if (addMutation.isPending()) {5 <span>Adding todo...</span>6 } @else if (addMutation.isError()) {7 <div>An error occurred: {{ addMutation.error()?.message }}</div>8 }9 <button (click)="addMutation.mutate('Item')">Add</button>10 `,11})12export class Todos {13 readonly #queryClient = inject(QueryClient)14 15 readonly addMutation = injectMutation(() => ({16 mutationFn: addTodo,17 onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }),18 }))19}通过 onMutate 实现乐观更新,并在 onError 中回滚:
1@Component({2 selector: 'todos',3 template: `<button (click)="addMutation.mutate('Item')">Add</button>`,4})5export class Todos {6 readonly #queryClient = inject(QueryClient)7 8 readonly addMutation = injectMutation(() => ({9 mutationFn: addTodo,10 onMutate: async (newTodo) => {11 await this.#queryClient.cancelQueries({ queryKey: ['todos'] })12 const previousTodos = this.#queryClient.getQueryData<Array<string>>(['todos'])13 14 this.#queryClient.setQueryData<Array<string>>(['todos'], (old) => [15 ...(old ?? []),16 newTodo,17 ])18 19 // Passed to `onError` as `onMutateResult` if the mutation fails.20 return { previousTodos }21 },22 onError: (_err, _newTodo, onMutateResult) => {23 this.#queryClient.setQueryData(['todos'], onMutateResult?.previousTodos)24 },25 onSettled: () => {26 this.#queryClient.invalidateQueries({ queryKey: ['todos'] })27 },28 }))29}传给 mutate 的每次调用的回调仅对最后一次调用生效 — mutateAsync 为每次调用提供一个 Promise,因此你可以在它们全部成功时等待全部结果:
1@Component({2 selector: 'todos',3 template: `4 <button (click)="handleAddAll(['Todo 1', 'Todo 2', 'Todo 3'])">Add all</button>5 `,6})7export class Todos {8 readonly #queryClient = inject(QueryClient)9 10 readonly addMutation = injectMutation(() => ({11 mutationFn: addTodo,12 onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }),13 }))14 15 async handleAddAll(todos: Array<string>) {16 try {17 await Promise.all(todos.map((todo) => this.addMutation.mutateAsync(todo)))18 } catch (error) {19 console.error('Failed to add todos:', error)20 }21 }22}如果上述某些修改可独立于其他修改而失败,且你想知道哪些失败了(而非在第一个拒绝时就丢失该信息),请将 Promise.all 替换为 Promise.allSettled:
1@Component({2 selector: 'todos',3 template: `4 <button (click)="handleAddAll(['Todo 1', 'Todo 2', 'Todo 3'])">Add all</button>5 `,6})7export class Todos {8 readonly #queryClient = inject(QueryClient)9 10 readonly addMutation = injectMutation(() => ({11 mutationFn: addTodo,12 onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }),13 }))14 15 async handleAddAll(todos: Array<string>) {16 const addResults = await Promise.allSettled(17 todos.map((todo) => this.addMutation.mutateAsync(todo)),18 )19 20 addResults.forEach((addResult, index) => {21 if (addResult.status === 'rejected') {22 console.error(`Failed to add "${todos[index]}":`, addResult.reason)23 }24 })25 }26}