TanStack Query(Svelte)MutationStateOptions类型全面解析:用useMutationState精准订阅 Mutation 状态
【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query
MutationStateOptions是 TanStack Query 在 Svelte 框架适配层(@tanstack/svelte-query)中为useMutationState定义的核心选项类型。它通过filters从全局 MutationCache 中筛选目标 mutation,再通过可选的select把原始MutationState投影为组件真正需要的返回值。读完本文,你将掌握该类型的完整签名、两个泛型参数的推导逻辑、底层过滤与订阅机制,并能直接写出可运行的 Svelte 5 代码来追踪"进行中的请求""已保存的数据""最新一次成功的变更"等真实场景。
类型签名总览
在 packages/svelte-query/src/types.ts 中,MutationStateOptions被定义为一个只有两个可选属性的对象类型:
/** Options for useMutationState */ export type MutationStateOptions< TResult = MutationState, TMutation extends Mutation<any, any, any, any> = MutationTypeFromResult<TResult>, > = { filters?: MutationFilters select?: (mutation: TMutation) => TResult }对应的 API 参考文档见 docs/framework/svelte/reference/type-aliases/MutationStateOptions.md。它是useMutationState的入参类型,而useMutationState的完整实现位于 packages/svelte-query/src/useMutationState.svelte.ts。
从签名可以看出,MutationStateOptions本身极其精简——它不直接存储任何 mutation 数据,而是描述"如何从 MutationCache 里挑选并转换数据"。所有筛选逻辑委托给filters(MutationFilters),所有转换逻辑委托给select。
泛型参数:从结果反推 Mutation 类型的类型体操
TResult:返回值投影类型
TResult = MutationStateTResult表示useMutationState最终返回数组中每一项的类型。默认值就是MutationState——即未提供select时,函数直接返回每个匹配 mutation 的state快照。
MutationState本身定义在 packages/query-core/src/mutation.ts,字段如下:
export interface MutationState< TData = unknown, TError = DefaultError, TVariables = unknown, TOnMutateResult = unknown, > { context: TOnMutateResult | undefined data: TData | undefined error: TError | null failureCount: number failureReason: TError | null isPaused: boolean status: MutationStatus variables: TVariables | undefined submittedAt: number }其中status的取值是MutationStatus('pending' | 'success' | 'error'等),submittedAt是提交时间戳。当select被提供时,TResult会被具体化为select的返回类型。
TMutation:由TResult反推的 Mutation 实例类型
TMutation extends Mutation<any, any, any, any> = MutationTypeFromResult<TResult>TMutation是select回调里mutation参数的类型。它默认通过MutationTypeFromResult<TResult>这个条件类型从TResult反推出来,定义在 packages/svelte-query/src/types.ts:
export type MutationTypeFromResult<TResult> = [TResult] extends [ MutationState< infer TData, infer TError, infer TVariables, infer TOnMutateResult >, ] ? Mutation<TData, TError, TVariables, TOnMutateResult> : Mutation这里的核心技巧是:
- 如果
TResult本身是MutationState<TData, TError, TVariables, TOnMutateResult>(或者可以从中推断出四个泛型),则TMutation被收紧为携带同样类型参数的Mutation<TData, TError, TVariables, TOnMutateResult>; - 否则退化为兜底的
Mutation(即Mutation<any, any, any, any>)。
由于TResult的默认值就是MutationState,所以默认情况下select拿到的mutation就是泛型完整的Mutation<TData, TError, TVariables, TOnMutateResult>,在 TS 严格模式下依然能拿到mutation.state.variables、mutation.state.data的精确类型,而不是any。这是保证useMutationState全链路类型安全的关键一环。
两个可选属性详解
filters?: MutationFilters—— 筛选维度
MutationFilters定义在 packages/query-core/src/utils.ts:
export interface MutationFilters< TData = unknown, TError = DefaultError, TVariables = unknown, TOnMutateResult = unknown, > { /** 是否精确匹配 mutation key(默认 false,见下文 findAll 说明) */ exact?: boolean /** 用谓词函数自由筛选 mutation */ predicate?: ( mutation: Mutation<TData, TError, TVariables, TOnMutateResult>, ) => boolean /** 按 mutation key 筛选(支持前缀匹配) */ mutationKey?: TuplePrefixes<MutationKey> /** 按状态筛选:pending / success / error */ status?: MutationStatus }各字段的实际作用:
mutationKey:按 key 过滤。注意它支持TuplePrefixes,即传入的 key 会按"前缀"语义匹配,例如['posts']可以匹配['posts', 'detail']。这是useMutationState最常用的过滤方式,用于跨组件定位某条业务线(如"所有发帖相关的 mutation")的全部 mutation 实例。exact:布尔值,表示是否要求mutationKey完全相等而非前缀匹配。在mutationCache.findAll内部,exact的默认行为见下节源码。status:按'pending' | 'success' | 'error'状态过滤,例如只想拿"正在提交中"的 mutation。predicate:最强大的兜底手段——一个接收Mutation实例、返回布尔值的函数,可对mutation.state、mutation.options等任意字段做自定义判断。
select?: (mutation: TMutation) => TResult—— 数据投影
optional select: (mutation) => TResult;select接收一个匹配到的Mutation实例(而非MutationState!),返回你想要的任意形状TResult。也就是说,select里可以访问mutation.state,也能访问mutation.options、mutation.mutationId等实例级信息。
它在底层的作用等价于数组的.map:
mutationCache .findAll(options.filters) .map((mutation) => options.select(mutation))不传select时,返回的每一项就是mutation.state(即默认TResult = MutationState)。
源码级原理:useMutationState如何消费这两个选项
useMutationState的实现(packages/svelte-query/src/useMutationState.svelte.ts)展示了filters与select在运行时被如何使用:
export function useMutationState< TResult = MutationState, TMutation extends Mutation<any, any, any, any> = MutationTypeFromResult<TResult>, >( options: MutationStateOptions<TResult, TMutation> = {}, queryClient?: QueryClient, ): Array<TResult> { const mutationCache = useQueryClient(queryClient).getMutationCache() const result = $state(getResult(mutationCache, options)) $effect(() => { const unsubscribe = mutationCache.subscribe(() => { const nextResult = replaceEqualDeep( result, getResult(mutationCache, options), ) if (result !== nextResult) { result.splice(0, result.length, ...nextResult) } }) return unsubscribe }) return result }核心步骤拆解:
- 取缓存:
useQueryClient(queryClient).getMutationCache()拿到当前 QueryClient 全局唯一的 MutationCache(可用第二参数指定自定义 QueryClient,否则取最近 context 中的那个)。 - 首次计算:
getResult立即执行一次,得到初始快照:function getResult<TResult, TMutation>( mutationCache: MutationCache, options: MutationStateOptions<TResult, TMutation>, ): Array<TResult> { return mutationCache .findAll(options.filters) // ① 用 filters 筛选 .map( (mutation): TResult => (options.select // ② 用 select 投影 ? options.select(mutation as TMutation) : mutation.state) as TResult, // ③ 缺省时取 state ) } - 响应式订阅:在
$effect中订阅mutationCache。每次有任何 mutation 发生变化(新增、pending、success、error、GC 移除等)都会触发重算。 - 稳定更新:重算结果用
replaceEqualDeep(来自@tanstack/query-core)与当前result做结构共享比较;只有内容真正变化时才原地更新$state数组,从而避免无关的 Svelte 重渲染。
这意味着useMutationState观察的是全局 MutationCache,因此能看到"由其他组件、其他 hook 实例创建、甚至已经卸载的 mutation"——这正是文档注释(packages/svelte-query/src/useMutationState.svelte.ts)强调的能力,也是它与createMutation(只关心单个 mutation)最本质的区别。
底层筛选:MutationCache.findAll与matchMutation
filters最终被交给 MutationCache 的方法,见 packages/query-core/src/mutationCache.ts:
findAll(filters: MutationFilters = {}): Array<Mutation> { return this.getAll().filter((mutation) => matchMutation(filters, mutation)) }matchMutation会依次对mutationKey(前缀或精确)、exact、status、predicate做匹配。注意这里与 Query 的find(mutationCache.ts)不同:find默认exact: true,而findAll默认exact: false(即默认按前缀匹配 key)。了解这个差异,能避免"为什么传['posts']却把['posts', 'x']也筛进来了"的困惑。
实战示例:三种高频用法
以下示例均取自useMutationState的官方 JSDoc(packages/svelte-query/src/useMutationState.svelte.ts),可直接在 Svelte 5 组件中使用。
示例一:获取所有进行中 mutation 的 variables
<script lang="ts"> import { useMutationState } from '@tanstack/svelte-query' const pendingVariables = useMutationState({ filters: { status: 'pending' }, select: (mutation) => mutation.state.variables, }) </script> {pendingVariables.length} posts saving...这里filters: { status: 'pending' }只筛出正在执行的重试/变更,select把每一项投影成variables,因此pendingVariables的类型是Array<unknown>(由TResult推导)。
示例二:通过mutationKey获取特定 mutation 的成功数据
<script lang="ts"> import { createMutation, useMutationState } from '@tanstack/svelte-query' const mutationKey = ['posts'] // 某个我们想跟踪状态的 mutation const mutation = createMutation(() => ({ mutationKey, mutationFn: createPosts, })) const savedPosts = useMutationState({ // 这个 key 必须与上方 mutation 的 key 一致 filters: { mutationKey, status: 'success' }, select: (mutation) => mutation.state.data, }) </script> <button onclick={() => mutation.mutate(['New Post'])}> Create post ({savedPosts.length} saved so far) </button>注意mutationKey与status: 'success'组合使用:mutationKey定位业务线,status排除掉 pending 与 error 的条目。
示例三:取"最新一次"成功变更的数据
<script lang="ts"> import { useMutationState } from '@tanstack/svelte-query' const savedPosts = useMutationState({ filters: { mutationKey: ['posts'], status: 'success' }, select: (mutation) => mutation.state.data, }) const latestSavedPost = $derived(savedPosts[savedPosts.length - 1]) </script> {latestSavedPost ? 'Saved' : 'Nothing saved yet'}原理:每次调用mutate都会向 MutationCache 写入一条新记录,并在gcTime(默认 5 分钟)后才会被回收。因此useMutationState返回的数组按时间顺序累积,取最后一项就是最近一次满足筛选条件的 mutation——这是文档与源码共同印证的行为(见 packages/svelte-query/src/useMutationState.svelte.ts)。
小结
MutationStateOptions虽然只有filters与select两个字段,却是useMutationState的"筛选 + 投影"双引擎:
| 维度 | 字段 | 作用 |
|---|---|---|
| 筛选 | filters | 交给MutationCache.findAll→matchMutation,支持mutationKey(前缀匹配)、exact、status、predicate |
| 投影 | select | 将Mutation实例转换为TResult,缺省时返回mutation.state |
| 类型 | TResult/TMutation | 通过MutationTypeFromResult条件类型反推select参数类型,保证全链路类型安全 |
结合 MutationTypeFromResult 与MutationState(packages/query-core/src/mutation.ts),你可以在 Svelte 5 中零成本地订阅全局 mutation 状态,无需为每个 mutation 单独维护本地变量——这正是 TanStack Query 服务端状态管理中"跨组件、跨生命周期观测变更"的核心能力。
【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考