禁用/暂停查询
如果你希望禁用某个查询,使其不再自动执行,可以使用 enabled = false 选项。enabled 选项还支持传入一个返回布尔值的回调函数。
当 enabled 为 false 时:
- 如果查询已有缓存数据,则该查询将初始化为
status === 'success'或isSuccess状态。 - 如果查询没有缓存数据,则该查询将从
status === 'pending'和fetchStatus === 'idle'状态开始。 - 查询在挂载时不会自动拉取数据。
- 查询不会在后台自动重新拉取。
- 查询会忽略查询客户端(QueryClient)的
invalidateQueries和refetchQueries调用——这些调用本应触发查询的重新拉取。 refetch(由useQuery返回)可用于手动触发查询拉取。但该方法不适用于skipToken。
TypeScript 用户可能更倾向于使用 skipToken 作为
enabled = false的替代方案。
1import { Switch, Match, Show, For } from 'solid-js'2 3function Todos() {4 const todosQuery = useQuery(() => ({5 queryKey: ['todos'],6 queryFn: fetchTodoList,7 enabled: false,8 }))9 10 return (11 <div>12 <button onClick={() => todosQuery.refetch()}>Fetch Todos</button>1314 <Switch>15 <Match when={todosQuery.data}>16 <ul>17 <For each={todosQuery.data}>{(todo) => <li>{todo.title}</li>}</For>18 </ul>19 </Match>20 <Match when={todosQuery.isError}>21 <span>Error: {todosQuery.error.message}</span>22 </Match>23 <Match when={todosQuery.isLoading}>24 <span>Loading...</span>25 </Match>26 <Match when={true}>27 <span>Not ready ...</span>28 </Match>29 </Switch>3031 <div>{todosQuery.isFetching ? 'Fetching...' : null}</div>32 </div>33 )34}永久禁用查询会使你无法使用 TanStack Query 提供的许多优秀功能(例如后台重新拉取),而且这也不是符合惯用法的做法。它会迫使你从声明式方式(定义查询应在何时运行)转向命令式方式(我点击此处时才拉取)。此外,你无法向 refetch 传递参数。很多时候,你真正需要的只是一个延迟查询(lazy query),即推迟初始拉取操作:
延迟查询(Lazy Queries)
enabled 选项不仅可以用于永久禁用查询,还可以在后续任意时间启用或禁用查询。一个典型示例是筛选表单:你仅希望在用户输入筛选值后才发起首次请求:
1import { createSignal } from 'solid-js'2 3function Todos() {4 const [filter, setFilter] = createSignal('')5 6 const todosQuery = useQuery(() => ({7 queryKey: ['todos', filter()],8 queryFn: () => fetchTodos(filter()),9 // ⬇️ disabled as long as the filter is empty10 enabled: !!filter(),11 }))12 13 return (14 <div>15 {/* 🚀 applying the filter will enable and execute the query */}16 <FiltersForm onApply={setFilter} />17 <Show when={todosQuery.data}>18 <TodosTable data={todosQuery.data} />19 </Show>20 </div>21 )22}isLoading(此前名为 isInitialLoading)
延迟查询从一开始就会处于 status: 'pending' 状态,因为 pending 意味着尚无数据。技术上这是正确的;但由于此时并未实际拉取任何数据(查询尚未 启用),因此你很可能无法利用该标志来显示加载指示器(loading spinner)。
如果你正在使用已禁用或延迟的查询,可改用 isLoading 标志。这是一个派生标志,其值根据以下条件计算得出:
isPending && isFetching
因此,仅当查询当前正进行首次拉取时,该标志才为 true。
使用 skipToken 进行类型安全的查询禁用
如果你正在使用 TypeScript,可以借助 skipToken 来禁用查询。当你需要基于某个条件禁用查询,同时又希望保持类型安全性时,这种方式非常有用。
重要提示:
refetch(由useQuery返回)无法与skipToken配合使用。对使用skipToken的查询调用refetch()将导致Missing queryFn错误,因为此时不存在有效的查询函数可供执行。如需手动触发查询,请考虑改用enabled: false,它能确保refetch()正常工作。除这一限制外,skipToken的行为与enabled: false完全一致。
1import { createSignal } from 'solid-js'2import { skipToken, useQuery } from '@tanstack/solid-query'3 4function Todos() {5 const [filter, setFilter] = createSignal<string | undefined>()6 7 const todosQuery = useQuery(() => ({8 queryKey: ['todos', filter()],9 // ⬇️ disabled as long as the filter is undefined or empty10 queryFn: filter() ? () => fetchTodos(filter()!) : skipToken,11 }))12 13 return (14 <div>15 {/* 🚀 applying the filter will enable and execute the query */}16 <FiltersForm onApply={setFilter} />17 <Show when={todosQuery.data}>18 <TodosTable data={todosQuery.data} />19 </Show>20 </div>21 )22}