2026/07/13
TanStack Query 的 useQuery 預設會在 Component 掛載後自動執行查詢。
但有些資料並不需要一進畫面就取得,例如:
這時可以使用 enabled 控制 Query 何時開始運作。
1useQuery({
2 queryKey: ['example'],
3 queryFn: fetchExample,
4 enabled: false,
5});當 enabled 為 false 時,Query 不會自動執行。
條件變成 true 後,TanStack Query 才會根據目前的快取狀態,決定是否發送請求。
假設頁面上有多個 Tab,每個 Tab 都有自己的子項目。
需求如下:
如果一進頁面就查詢所有 Tab 的子資料,會產生許多不必要的 API Request。
這種情境很適合使用 enabled。
1import { useQuery } from '@tanstack/react-query';
2
3interface UseTabSinglesParams {
4 laboratoryId: string;
5 enabled: boolean;
6}
7
8export function useTabSingles({
9 laboratoryId,
10 enabled,
11}: UseTabSinglesParams) {
12 return useQuery({
13 queryKey: ['laboratory', laboratoryId, 'singles'],
14 queryFn: () => getLaboratorySingles(laboratoryId),
15 enabled: enabled && Boolean(laboratoryId),
16 staleTime: 5 * 60 * 1000,
17 });
18}這裡的 Query 只有在兩個條件都成立時才會啟用:
1enabled && Boolean(laboratoryId)也就是:
laboratoryId 已經存在這可以避免參數尚未準備完成時,送出無效請求。
1import { useState } from 'react';
2
3interface TabItemFlyoutProps {
4 laboratoryId: string;
5 isActive: boolean;
6}
7
8export function TabItemFlyout({
9 laboratoryId,
10 isActive,
11}: TabItemFlyoutProps) {
12 const [isFlyoutOpen, setIsFlyoutOpen] = useState(false);
13
14 const shouldFetchSingles =
15 isFlyoutOpen && !isActive;
16
17 const {
18 data: remoteSingles,
19 isPending,
20 isFetching,
21 isError,
22 } = useTabSingles({
23 laboratoryId,
24 enabled: shouldFetchSingles,
25 });
26
27 const isInitialLoading =
28 shouldFetchSingles &&
29 isPending &&
30 isFetching;
31
32 return (
33 <div
34 onMouseEnter={() => setIsFlyoutOpen(true)}
35 onMouseLeave={() => setIsFlyoutOpen(false)}
36 >
37 <TabItem />
38
39 {isFlyoutOpen && (
40 <TabSingleFlyout
41 singles={remoteSingles ?? []}
42 isLoading={isInitialLoading}
43 isError={isError}
44 />
45 )}
46 </div>
47 );
48}最重要的是這段:
1const shouldFetchSingles =
2 isFlyoutOpen && !isActive;它表示:
Flyout 已經開啟,而且目前不是 Active Tab 時,才查詢遠端資料。
Active Tab 的資料通常已經由目前頁面取得,因此不需要再次請求。
不一定。
假設設定如下:
1useQuery({
2 queryKey: ['laboratory', laboratoryId, 'singles'],
3 queryFn: () => getLaboratorySingles(laboratoryId),
4 enabled: shouldFetchSingles,
5 staleTime: 5 * 60 * 1000,
6});第一次 Hover:
1Cache 沒有資料
2→ enabled 變成 true
3→ 發送 API Request
4→ 結果存入 Cache短時間內再次 Hover:
1Cache 已有新鮮資料
2→ enabled 變成 true
3→ 直接使用 Cache
4→ 不重新發送請求超過 staleTime 後再次 Hover:
1Cache 資料已過期
2→ 使用既有 Cache
3→ 可能進行背景更新因此,enabled 只是控制 Query 是否允許自動執行。
實際是否重新請求,仍然取決於:
staleTimeTanStack Query 預設會把查詢結果視為 stale。
如果 Flyout 子資料不需要每次 Hover 都更新,可以設定:
1staleTime: 5 * 60 * 1000代表資料在 5 分鐘內會被視為新鮮資料。
| 操作 | 查詢行為 |
|---|---|
| 第一次 Hover | 發送請求 |
| 5 分鐘內再次 Hover | 使用 Cache,不重新請求 |
| 超過 5 分鐘再次 Hover | 可能背景更新 |
| Hover 不同 Tab | 使用不同 Query Key 查詢 |
Query Key 必須包含 laboratoryId:
1queryKey: ['laboratory', laboratoryId, 'singles']這樣每個 Tab 才會有各自獨立的快取。
延遲查詢時,不能只看 isPending。
當 Query 尚未執行,而且 Cache 沒有資料時,即使 enabled 是 false,Query 仍可能是 pending 狀態。
因此第一次載入可以這樣判斷:
1const isInitialLoading =
2 shouldFetchSingles &&
3 isPending &&
4 isFetching;| 狀態 | 意義 |
|---|---|
isPending | 尚未成功取得過資料 |
isFetching | Query Function 正在執行 |
| 兩者皆為 true | 正在進行第一次載入 |
這可以避免 Flyout 尚未開啟時就顯示 Loading。
1useQuery({
2 queryKey: ['user', userId],
3 queryFn: () => getUser(userId),
4 enabled: Boolean(userId),
5});1const userQuery = useQuery({
2 queryKey: ['user', email],
3 queryFn: () => getUserByEmail(email),
4});
5
6const projectsQuery = useQuery({
7 queryKey: ['projects', userQuery.data?.id],
8 queryFn: () => getProjects(userQuery.data!.id),
9 enabled: Boolean(userQuery.data?.id),
10});1useQuery({
2 queryKey: ['item-detail', itemId],
3 queryFn: () => getItemDetail(itemId),
4 enabled: isModalOpen,
5});1useQuery({
2 queryKey: ['current-user'],
3 queryFn: getCurrentUser,
4 enabled: authStatus === 'authenticated',
5});錯誤:
1useQuery({
2 queryKey: ['singles'],
3 queryFn: () => getLaboratorySingles(laboratoryId),
4});不同 Tab 會共用同一份 Cache,可能顯示錯誤資料。
正確:
1useQuery({
2 queryKey: ['laboratory', laboratoryId, 'singles'],
3 queryFn: () => getLaboratorySingles(laboratoryId),
4});不建議:
1useEffect(() => {
2 if (isFlyoutOpen) {
3 refetch();
4 }
5}, [isFlyoutOpen, refetch]);可以直接宣告 Query 的啟用條件:
1useQuery({
2 queryKey,
3 queryFn,
4 enabled: isFlyoutOpen,
5});這樣查詢條件會更清楚,也更符合 TanStack Query 的使用方式。
enabled 適合用來描述:
當某個條件成立後,這筆 Query 才應該開始運作。
在 TabItemFlyout 的案例中:
1const shouldFetchSingles =
2 isFlyoutOpen &&
3 !isActive &&
4 Boolean(laboratoryId);可以達成:
laboratoryId 分開快取staleTime 避免重複請求useEffect 手動控制查詢可以把 enabled 理解成 Query 的閘門:
1條件不成立
2→ Query 保持待命
3
4條件成立
5→ 檢查 Cache 與 staleTime
6→ 必要時才發送請求