依賴查詢

useQuery 相依查詢 (Dependent Query)

相依查詢 (或稱序列查詢) 需要等待前一個查詢完成後才能執行。實現方式很簡單,只需使用 enabled 選項來告訴查詢何時可以執行:

js
// 取得使用者資料
const { data: user } = useQuery({
  queryKey: ['user', email],
  queryFn: () => getUserByEmail(email.value),
})

const userId = computed(() => user.value?.id)
const enabled = computed(() => !!user.value?.id)

// 接著取得使用者的專案資料
const { isIdle, data: projects } = useQuery({
  queryKey: ['projects', userId],
  queryFn: () => getProjectsByUser(userId.value),
  enabled, // 此查詢在 `enabled == true` 前不會執行
})
// 取得使用者資料
const { data: user } = useQuery({
  queryKey: ['user', email],
  queryFn: () => getUserByEmail(email.value),
})

const userId = computed(() => user.value?.id)
const enabled = computed(() => !!user.value?.id)

// 接著取得使用者的專案資料
const { isIdle, data: projects } = useQuery({
  queryKey: ['projects', userId],
  queryFn: () => getProjectsByUser(userId.value),
  enabled, // 此查詢在 `enabled == true` 前不會執行
})

projects 查詢會以以下狀態開始:

tsx
status: 'pending'
isPending: true
fetchStatus: 'idle'
status: 'pending'
isPending: true
fetchStatus: 'idle'

user 資料可用時,projects 查詢會被 enabled 並轉換為:

tsx
status: 'pending'
isPending: true
fetchStatus: 'fetching'
status: 'pending'
isPending: true
fetchStatus: 'fetching'

取得專案資料後,狀態會變為:

tsx
status: 'success'
isPending: false
fetchStatus: 'idle'
status: 'success'
isPending: false
fetchStatus: 'idle'

useQueries 相依查詢 (Dependent Query)

動態平行查詢 - useQueries 也可以依賴前一個查詢,實現方式如下:

tsx
// 取得使用者 ID 列表
const { data: userIds } = useQuery({
  queryKey: ['users'],
  queryFn: getUsersData,
  select: (users) => users.map((user) => user.id),
})

const queries = computed(() => {
  return userIds.value.length
    ? userIds.value.map((id) => {
        return {
          queryKey: ['messages', id],
          queryFn: () => getMessagesByUsers(id),
        }
      })
    : []
})

// 接著取得使用者的訊息資料
const usersMessages = useQueries({
  queries, // 如果 users 是 undefined,會回傳空陣列
})
// 取得使用者 ID 列表
const { data: userIds } = useQuery({
  queryKey: ['users'],
  queryFn: getUsersData,
  select: (users) => users.map((user) => user.id),
})

const queries = computed(() => {
  return userIds.value.length
    ? userIds.value.map((id) => {
        return {
          queryKey: ['messages', id],
          queryFn: () => getMessagesByUsers(id),
        }
      })
    : []
})

// 接著取得使用者的訊息資料
const usersMessages = useQueries({
  queries, // 如果 users 是 undefined,會回傳空陣列
})

注意 useQueries 會回傳一個查詢結果陣列

關於效能的注意事項

相依查詢本質上會形成一種請求瀑布流 (request waterfall),這會影響效能。假設兩個查詢花費相同時間,序列執行總會比平行執行多花一倍時間,對於高延遲的客戶端影響尤其嚴重。如果可能,最好重構後端 API 讓兩個查詢能平行取得,雖然這在實務上不一定可行。

在上面的範例中,與其先執行 getUserByEmail 才能執行 getProjectsByUser,引入新的 getProjectsByUserEmail 查詢可以消除瀑布流問題。