mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-21 09:42:09 +08:00
refactor: optimize swr data refresh paths
This commit is contained in:
@@ -18,7 +18,7 @@ import {
|
||||
} from '@/providers/app-data-context'
|
||||
import { patchClashMode } from '@/services/cmds'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { setCacheData } from '@/services/query-client'
|
||||
import type { TranslationKey } from '@/types/generated/i18n-keys'
|
||||
|
||||
const CLASH_MODES = ['rule', 'global', 'direct'] as const
|
||||
@@ -114,9 +114,8 @@ export const ClashModeCard = () => {
|
||||
// 成功:写穿主源缓存,使实时 mode 立即反映新值——即使随后的 /configs refetch
|
||||
// 失败(TanStack 会保留旧 data),controllerMode 也不会再压过新值导致闪回。
|
||||
// 若主源从未成功过(old 为 undefined)则保持不动,改由兜底来源反映。
|
||||
queryClient.setQueryData<BaseConfig | undefined>(
|
||||
['getClashConfig'],
|
||||
(old) => (old ? { ...old, mode } : old),
|
||||
setCacheData<BaseConfig>(['getClashConfig'], (old) =>
|
||||
old ? { ...old, mode } : old,
|
||||
)
|
||||
// 刷新主源与兜底源以与后端对齐,待数据落地后再清除乐观状态
|
||||
await Promise.allSettled([refreshClashConfig(), refetchBackendMode()])
|
||||
|
||||
@@ -425,7 +425,6 @@ function useIPInfo() {
|
||||
queryKey: [IP_INFO_CACHE_KEY],
|
||||
queryFn: getIpInfo,
|
||||
staleTime: Infinity,
|
||||
gcTime: 60 * 60 * 1000,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
retry: 1,
|
||||
|
||||
+10
-7
@@ -7,8 +7,12 @@ import {
|
||||
getRuntimeConfig,
|
||||
patchClashConfig,
|
||||
} from '@/services/cmds'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import {
|
||||
getCacheData,
|
||||
revalidateQuery,
|
||||
setCacheData,
|
||||
useQuery,
|
||||
} from '@/services/query-client'
|
||||
|
||||
type MutateClashUpdater =
|
||||
| ((old: IConfigData | undefined) => IConfigData | undefined)
|
||||
@@ -90,9 +94,9 @@ export const useClash = () => {
|
||||
}
|
||||
const next =
|
||||
typeof updater === 'function'
|
||||
? updater(queryClient.getQueryData<IConfigData>(['getRuntimeConfig']))
|
||||
? updater(getCacheData<IConfigData>(['getRuntimeConfig']))
|
||||
: updater
|
||||
queryClient.setQueryData(['getRuntimeConfig'], next)
|
||||
setCacheData(['getRuntimeConfig'], next)
|
||||
if (revalidate !== false) {
|
||||
return refetch()
|
||||
}
|
||||
@@ -130,11 +134,10 @@ export const useClashInfo = () => {
|
||||
|
||||
await patchClashConfig(patch)
|
||||
mutateInfo()
|
||||
queryClient.invalidateQueries({ queryKey: ['getClashConfig'] })
|
||||
revalidateQuery(['getClashConfig'])
|
||||
})
|
||||
|
||||
const invalidateClashConfig = () =>
|
||||
queryClient.invalidateQueries({ queryKey: ['getClashConfig'] })
|
||||
const invalidateClashConfig = () => revalidateQuery(['getClashConfig'])
|
||||
|
||||
return {
|
||||
clashInfo,
|
||||
|
||||
@@ -42,7 +42,6 @@ export const useIconCache = ({
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
staleTime: Infinity,
|
||||
gcTime: 30 * 60 * 1000,
|
||||
retry: 2,
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useRef } from 'react'
|
||||
import { MihomoWebSocket, type LogLevel } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { getClashLogs } from '@/services/cmds'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { setCacheData } from '@/services/query-client'
|
||||
|
||||
import { useClashLog } from './use-clash-log'
|
||||
import { useMihomoWsSubscription } from './use-mihomo-ws-subscription'
|
||||
@@ -155,7 +155,7 @@ export const useLogData = () => {
|
||||
const refreshGetClashLog = (clear = false) => {
|
||||
if (clear) {
|
||||
if (subscriptionCacheKey) {
|
||||
queryClient.setQueryData<ILogItem[]>([subscriptionCacheKey], [])
|
||||
setCacheData<ILogItem[]>([subscriptionCacheKey], [])
|
||||
}
|
||||
} else {
|
||||
hasLoadedInitialLogsRef.current = false
|
||||
|
||||
@@ -2,7 +2,12 @@ import { useLocalStorage } from 'foxact/use-local-storage'
|
||||
import { type MutableRefObject, useCallback, useEffect, useRef } from 'react'
|
||||
import { type Message, type MihomoWebSocket } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { queryClient, useQuery } from '@/services/query-client'
|
||||
import {
|
||||
getCacheData,
|
||||
removeCacheData,
|
||||
setCacheData,
|
||||
useQuery,
|
||||
} from '@/services/query-client'
|
||||
|
||||
export const RECONNECT_DELAY_MS = 1000
|
||||
|
||||
@@ -137,7 +142,7 @@ const createSharedSubscriptionEntry = (
|
||||
/**
|
||||
* Mirrors SWR's MutatorCallback: consumers can pass either a plain value or a
|
||||
* functional updater `(current?: T) => T`. The functional form is resolved
|
||||
* against the current cache entry before calling `queryClient.setQueryData`.
|
||||
* against the current cache entry before calling `setCacheData`.
|
||||
*/
|
||||
type NextFn<T> = (
|
||||
error?: any,
|
||||
@@ -205,7 +210,7 @@ export const useMihomoWsSubscription = <T>(
|
||||
): T => {
|
||||
if (typeof data === 'function') {
|
||||
const updater = data as (current?: T) => T | undefined
|
||||
const current = queryClient.getQueryData<T>([cacheKey])
|
||||
const current = getCacheData<T>([cacheKey])
|
||||
return updater(current) ?? fallbackData
|
||||
}
|
||||
return data ?? fallbackData
|
||||
@@ -215,13 +220,11 @@ export const useMihomoWsSubscription = <T>(
|
||||
|
||||
const response = useQuery<T>({
|
||||
queryKey: responseCacheKey ? [responseCacheKey] : ['$sub$__disabled__'],
|
||||
queryFn: () =>
|
||||
queryClient.getQueryData<T>([responseCacheKey!]) ?? fallbackData,
|
||||
queryFn: () => getCacheData<T>([responseCacheKey!]) ?? fallbackData,
|
||||
initialData: () =>
|
||||
queryClient.getQueryData<T>([responseCacheKey ?? '$sub$__disabled__']) ??
|
||||
getCacheData<T>([responseCacheKey ?? '$sub$__disabled__']) ??
|
||||
fallbackData,
|
||||
staleTime: Infinity,
|
||||
gcTime: 30_000,
|
||||
enabled: subscriptionCacheKey !== null,
|
||||
})
|
||||
|
||||
@@ -248,7 +251,7 @@ export const useMihomoWsSubscription = <T>(
|
||||
}
|
||||
if (data === undefined) return
|
||||
const resolved = resolveNextData(data, subscriptionCacheKey)
|
||||
queryClient.setQueryData<T>([subscriptionCacheKey], resolved)
|
||||
setCacheData<T>([subscriptionCacheKey], resolved)
|
||||
}
|
||||
|
||||
if (throttleMs && throttleMs > 0) {
|
||||
@@ -351,7 +354,7 @@ export const useMihomoWsSubscription = <T>(
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
if (subscriptionCacheKey) {
|
||||
queryClient.removeQueries({ queryKey: [subscriptionCacheKey] })
|
||||
removeCacheData([subscriptionCacheKey])
|
||||
}
|
||||
setDate(Date.now())
|
||||
}, [subscriptionCacheKey, setDate])
|
||||
|
||||
@@ -6,8 +6,7 @@ import {
|
||||
patchProfile,
|
||||
patchProfilesConfig,
|
||||
} from '@/services/cmds'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { setCacheData, useQuery } from '@/services/query-client'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
|
||||
export const useProfiles = () => {
|
||||
@@ -201,7 +200,7 @@ export const useProfiles = () => {
|
||||
await patchProfile(current.uid, { selected: newSelected })
|
||||
debugLog('[ActivateSelected] 代理选择配置保存成功')
|
||||
|
||||
queryClient.setQueryData(['getProxies'], await calcuProxies())
|
||||
setCacheData(['getProxies'], await calcuProxies())
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[ActivateSelected] 保存代理选择配置失败:',
|
||||
|
||||
@@ -4,8 +4,7 @@ import { closeAllConnections } from 'tauri-plugin-mihomo-api'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useClashConfigData, useSystemData } from '@/providers/app-data-context'
|
||||
import { getAutotemProxy } from '@/services/cmds'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { revalidateQueries, useQuery } from '@/services/query-client'
|
||||
|
||||
// 系统代理状态检测统一逻辑
|
||||
export const useSystemProxyState = () => {
|
||||
@@ -65,18 +64,12 @@ export const useSystemProxyState = () => {
|
||||
}
|
||||
} finally {
|
||||
busyRef.current = false
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['getSystemProxy'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['getAutotemProxy'] }),
|
||||
])
|
||||
await revalidateQueries([['getSystemProxy'], ['getAutotemProxy']])
|
||||
}
|
||||
}
|
||||
|
||||
const invalidateProxyState = () =>
|
||||
Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['getSystemProxy'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['getAutotemProxy'] }),
|
||||
])
|
||||
revalidateQueries([['getSystemProxy'], ['getAutotemProxy']])
|
||||
|
||||
return {
|
||||
indicator,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { showNotice } from '@/services/notice-service'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
|
||||
import { useVerge } from './use-verge'
|
||||
import { useVisibility } from './use-visibility'
|
||||
|
||||
export interface SystemState {
|
||||
runningMode: 'Sidecar' | 'Service'
|
||||
@@ -27,6 +28,7 @@ const STARTUP_GRACE_MS = 10_000
|
||||
*/
|
||||
export function useSystemState() {
|
||||
const { verge, patchVerge } = useVerge()
|
||||
const pageVisible = useVisibility()
|
||||
const disablingTunRef = useRef(false)
|
||||
const [isStartingUp, setIsStartingUp] = useState(true)
|
||||
|
||||
@@ -49,7 +51,9 @@ export function useSystemState() {
|
||||
])
|
||||
return { runningMode, isAdminMode, isServiceOk } as SystemState
|
||||
},
|
||||
refetchInterval: isStartingUp ? 2000 : 30000,
|
||||
refetchInterval: pageVisible ? (isStartingUp ? 2000 : 30000) : false,
|
||||
refetchOnWindowFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
})
|
||||
|
||||
const isSidecarMode = systemState.runningMode === 'Sidecar'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { setCacheData, useQuery } from '@/services/query-client'
|
||||
import { checkUpdateSafe } from '@/services/update'
|
||||
|
||||
import { useVerge } from './use-verge'
|
||||
@@ -24,7 +23,7 @@ export const readLastCheckTime = (): number | null => {
|
||||
export const updateLastCheckTime = (timestamp?: number): number => {
|
||||
const now = timestamp ?? Date.now()
|
||||
localStorage.setItem(LAST_CHECK_KEY, now.toString())
|
||||
queryClient.setQueryData([LAST_CHECK_KEY], now)
|
||||
setCacheData([LAST_CHECK_KEY], now)
|
||||
return now
|
||||
}
|
||||
|
||||
@@ -54,6 +53,7 @@ export const useUpdate = (enabled: boolean = true) => {
|
||||
retry: 2,
|
||||
staleTime: 60 * 60 * 1000,
|
||||
refetchInterval: 24 * 60 * 60 * 1000,
|
||||
refetchIntervalInBackground: false,
|
||||
refetchOnWindowFocus: false,
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback } from 'react'
|
||||
|
||||
import { getVergeConfig, patchVergeConfig } from '@/services/cmds'
|
||||
import { getPreloadConfig, setPreloadConfig } from '@/services/preload'
|
||||
import { queryClient, useQuery } from '@/services/query-client'
|
||||
import { getCacheData, setCacheData, useQuery } from '@/services/query-client'
|
||||
|
||||
export const useVerge = () => {
|
||||
const initialVergeConfig = getPreloadConfig()
|
||||
@@ -30,11 +30,11 @@ export const useVerge = () => {
|
||||
return
|
||||
}
|
||||
if (typeof updaterOrData === 'function') {
|
||||
const prev = queryClient.getQueryData<IVergeConfig>(['getVergeConfig'])
|
||||
const prev = getCacheData<IVergeConfig>(['getVergeConfig'])
|
||||
const next = updaterOrData(prev)
|
||||
queryClient.setQueryData(['getVergeConfig'], next)
|
||||
setCacheData(['getVergeConfig'], next)
|
||||
} else {
|
||||
queryClient.setQueryData(['getVergeConfig'], updaterOrData)
|
||||
setCacheData(['getVergeConfig'], updaterOrData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ import {
|
||||
resolveThemeMode,
|
||||
getPreloadConfig,
|
||||
} from './services/preload'
|
||||
import { queryClientConfig } from './services/query-client'
|
||||
import { swrConfig } from './services/query-client'
|
||||
import {
|
||||
LoadingCacheProvider,
|
||||
ThemeModeProvider,
|
||||
@@ -51,7 +51,7 @@ const initializeApp = (initialThemeMode: 'light' | 'dark') => {
|
||||
<React.StrictMode>
|
||||
<ComposeContextProvider contexts={contexts}>
|
||||
<BaseErrorBoundary>
|
||||
<SWRConfig value={queryClientConfig}>
|
||||
<SWRConfig value={swrConfig}>
|
||||
<WindowProvider>
|
||||
<AppDataProvider>
|
||||
<RouterProvider router={router} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { useListen } from '@/hooks/use-listen'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { revalidateQueries } from '@/services/query-client'
|
||||
|
||||
export const useLayoutEvents = (
|
||||
handleNotice: (payload: [string, string]) => void,
|
||||
@@ -12,9 +12,7 @@ export const useLayoutEvents = (
|
||||
const unlisteners: Array<() => void> = []
|
||||
let disposed = false
|
||||
const revalidateKeys = (keys: readonly string[]) => {
|
||||
keys.forEach((key) => {
|
||||
queryClient.invalidateQueries({ queryKey: [key] })
|
||||
})
|
||||
void revalidateQueries(keys.map((key) => [key]))
|
||||
}
|
||||
|
||||
const register = (
|
||||
@@ -42,7 +40,7 @@ export const useLayoutEvents = (
|
||||
}
|
||||
|
||||
register(
|
||||
addListener('verge://refresh-clash-config', async () => {
|
||||
addListener('verge://refresh-clash-config', () => {
|
||||
revalidateKeys([
|
||||
'getProxies',
|
||||
'getVersion',
|
||||
|
||||
+10
-12
@@ -64,8 +64,12 @@ import {
|
||||
updateProfile,
|
||||
} from '@/services/cmds'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import {
|
||||
fetchCacheData,
|
||||
revalidateQueries,
|
||||
setCacheData,
|
||||
useQuery,
|
||||
} from '@/services/query-client'
|
||||
import {
|
||||
useLoadingCache,
|
||||
useSetLoadingCache,
|
||||
@@ -233,7 +237,7 @@ const ProfilePage = () => {
|
||||
return () => {
|
||||
unsubscribe.then((cleanup) => cleanup())
|
||||
}
|
||||
}, [addListener, mutateProfiles, t])
|
||||
}, [addListener, mutateProfiles])
|
||||
|
||||
// 添加紧急恢复功能
|
||||
const onEmergencyRefresh = useLockFn(async () => {
|
||||
@@ -241,10 +245,7 @@ const ProfilePage = () => {
|
||||
|
||||
try {
|
||||
// 只失效 profiles 相关 query,不影响 WS 订阅、IP 缓存等其他 query
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['getProfiles'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['getRuntimeLogs'] }),
|
||||
])
|
||||
await revalidateQueries([['getProfiles'], ['getRuntimeLogs']])
|
||||
|
||||
// 强制重新获取配置数据
|
||||
await mutateProfiles()
|
||||
@@ -371,10 +372,7 @@ const ProfilePage = () => {
|
||||
console.warn(`[导入刷新] 常规刷新失败,尝试清除缓存重新获取`)
|
||||
try {
|
||||
// 清除缓存并重新获取
|
||||
await queryClient.fetchQuery({
|
||||
queryKey: ['getProfiles'],
|
||||
queryFn: getProfiles,
|
||||
})
|
||||
await fetchCacheData(['getProfiles'], getProfiles)
|
||||
await onEnhance(false)
|
||||
showNotice.error(
|
||||
'profiles.page.feedback.notifications.importNeedsRefresh',
|
||||
@@ -482,7 +480,7 @@ const ProfilePage = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
queryClient.setQueryData(['getProxies'], await calcuProxies())
|
||||
setCacheData(['getProxies'], await calcuProxies())
|
||||
|
||||
// 完成切换
|
||||
await mutateLogs()
|
||||
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
getRunningMode,
|
||||
getSystemProxy,
|
||||
} from '@/services/cmds'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
import { revalidateQueries, useQuery } from '@/services/query-client'
|
||||
|
||||
import {
|
||||
ClashConfigContext,
|
||||
@@ -138,11 +137,13 @@ export const AppDataProvider = ({
|
||||
}
|
||||
lastProfileId = newProfileId
|
||||
lastUpdateTime = now
|
||||
void queryClient.invalidateQueries({ queryKey: ['getProfiles'] })
|
||||
refreshProxy().catch(() => {})
|
||||
refreshProxyProviders().catch(() => {})
|
||||
refreshRules().catch(() => {})
|
||||
refreshRuleProviders().catch(() => {})
|
||||
void Promise.allSettled([
|
||||
revalidateQueries([['getProfiles']]),
|
||||
refreshProxy(),
|
||||
refreshProxyProviders(),
|
||||
refreshRules(),
|
||||
refreshRuleProviders(),
|
||||
])
|
||||
}
|
||||
|
||||
const handleRefreshProxy = () => {
|
||||
|
||||
@@ -14,7 +14,6 @@ type QueryOptions<T> = {
|
||||
initialData?: T | (() => T | undefined)
|
||||
placeholderData?: T | (() => T | undefined)
|
||||
staleTime?: number
|
||||
gcTime?: number
|
||||
retry?: number | false
|
||||
retryDelay?: number | ((attempt: number) => number)
|
||||
refetchInterval?: number | false
|
||||
@@ -23,106 +22,82 @@ type QueryOptions<T> = {
|
||||
refetchOnReconnect?: boolean
|
||||
}
|
||||
|
||||
type QueryResult<T> = Omit<SWRResponse<T>, 'mutate'> & {
|
||||
type QueryResult<T> = SWRResponse<T> & {
|
||||
isFetching: boolean
|
||||
isPending: boolean
|
||||
refetch: () => Promise<{ data: T | undefined }>
|
||||
mutate: SWRResponse<T>['mutate']
|
||||
}
|
||||
|
||||
type QueryFilters = {
|
||||
queryKey: QueryKey
|
||||
}
|
||||
|
||||
const keyToSWRKey = (queryKey: QueryKey) => {
|
||||
return Array.isArray(queryKey) ? [...queryKey] : queryKey
|
||||
}
|
||||
|
||||
const serializeQueryKey = (queryKey: QueryKey) => {
|
||||
return unstable_serialize(keyToSWRKey(queryKey))
|
||||
}
|
||||
|
||||
const resolveFallbackData = <T>(
|
||||
initialData: QueryOptions<T>['initialData'],
|
||||
placeholderData: QueryOptions<T>['placeholderData'],
|
||||
) => {
|
||||
const data = initialData ?? placeholderData
|
||||
return typeof data === 'function' ? (data as () => T | undefined)() : data
|
||||
}
|
||||
|
||||
const resolveRetryInterval = (
|
||||
retryDelay: QueryOptions<unknown>['retryDelay'],
|
||||
) => {
|
||||
if (typeof retryDelay !== 'function') return retryDelay
|
||||
return retryDelay(0)
|
||||
}
|
||||
const serializeQueryKey = (queryKey: QueryKey) => unstable_serialize(queryKey)
|
||||
|
||||
export const queryCache = new Map<string, unknown>()
|
||||
|
||||
export const queryClientConfig: SWRConfiguration = {
|
||||
const setCachedData = <T>(queryKey: QueryKey, data: T | undefined) => {
|
||||
const cacheKey = serializeQueryKey(queryKey)
|
||||
if (data === undefined) {
|
||||
queryCache.delete(cacheKey)
|
||||
} else {
|
||||
queryCache.set(cacheKey, data)
|
||||
}
|
||||
}
|
||||
|
||||
export const swrConfig: SWRConfiguration = {
|
||||
dedupingInterval: 2000,
|
||||
errorRetryCount: 3,
|
||||
errorRetryInterval: 5000,
|
||||
revalidateOnFocus: false,
|
||||
}
|
||||
|
||||
export const queryClient = {
|
||||
getQueryData<T>(queryKey: QueryKey): T | undefined {
|
||||
return queryCache.get(serializeQueryKey(queryKey)) as T | undefined
|
||||
},
|
||||
|
||||
setQueryData<T>(
|
||||
queryKey: QueryKey,
|
||||
updaterOrData: T | undefined | ((current: T | undefined) => T | undefined),
|
||||
) {
|
||||
const current = this.getQueryData<T>(queryKey)
|
||||
const next =
|
||||
typeof updaterOrData === 'function'
|
||||
? (updaterOrData as (current: T | undefined) => T | undefined)(current)
|
||||
: updaterOrData
|
||||
|
||||
if (next === undefined) {
|
||||
queryCache.delete(serializeQueryKey(queryKey))
|
||||
} else {
|
||||
queryCache.set(serializeQueryKey(queryKey), next)
|
||||
}
|
||||
|
||||
void swrMutate(keyToSWRKey(queryKey), next, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
return next
|
||||
},
|
||||
|
||||
invalidateQueries({ queryKey }: QueryFilters) {
|
||||
return swrMutate(keyToSWRKey(queryKey))
|
||||
},
|
||||
|
||||
removeQueries({ queryKey }: QueryFilters) {
|
||||
queryCache.delete(serializeQueryKey(queryKey))
|
||||
return swrMutate(keyToSWRKey(queryKey), undefined, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
},
|
||||
|
||||
async fetchQuery<T>({
|
||||
queryKey,
|
||||
queryFn,
|
||||
}: {
|
||||
queryKey: QueryKey
|
||||
queryFn: () => Promise<T> | T
|
||||
}) {
|
||||
const data = await queryFn()
|
||||
this.setQueryData(queryKey, data)
|
||||
return data
|
||||
},
|
||||
export const getCacheData = <T>(queryKey: QueryKey): T | undefined => {
|
||||
return queryCache.get(serializeQueryKey(queryKey)) as T | undefined
|
||||
}
|
||||
|
||||
export const invalidateQueries = (queryKeys: readonly QueryKey[]) =>
|
||||
Promise.all(
|
||||
queryKeys.map((queryKey) => queryClient.invalidateQueries({ queryKey })),
|
||||
)
|
||||
export const setCacheData = <T>(
|
||||
queryKey: QueryKey,
|
||||
updaterOrData: T | undefined | ((current: T | undefined) => T | undefined),
|
||||
) => {
|
||||
const current = getCacheData<T>(queryKey)
|
||||
const next =
|
||||
typeof updaterOrData === 'function'
|
||||
? (updaterOrData as (current: T | undefined) => T | undefined)(current)
|
||||
: updaterOrData
|
||||
|
||||
setCachedData(queryKey, next)
|
||||
|
||||
void swrMutate(queryKey, next, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
return next
|
||||
}
|
||||
|
||||
export const revalidateQuery = async (queryKey: QueryKey) => {
|
||||
const data = await swrMutate(queryKey)
|
||||
if (data !== undefined) {
|
||||
setCachedData(queryKey, data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export const revalidateQueries = (queryKeys: readonly QueryKey[]) =>
|
||||
Promise.all(queryKeys.map(revalidateQuery))
|
||||
|
||||
export const removeCacheData = (queryKey: QueryKey) => {
|
||||
setCachedData(queryKey, undefined)
|
||||
return swrMutate(queryKey, undefined, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
}
|
||||
|
||||
export const fetchCacheData = async <T>(
|
||||
queryKey: QueryKey,
|
||||
queryFn: () => Promise<T> | T,
|
||||
) => {
|
||||
const data = await queryFn()
|
||||
setCacheData(queryKey, data)
|
||||
return data
|
||||
}
|
||||
|
||||
export function useQuery<T>(options: QueryOptions<T>): QueryResult<T> {
|
||||
const {
|
||||
@@ -140,24 +115,44 @@ export function useQuery<T>(options: QueryOptions<T>): QueryResult<T> {
|
||||
staleTime,
|
||||
} = options
|
||||
|
||||
const fallbackData = resolveFallbackData(initialData, placeholderData)
|
||||
const fallbackDataSource = initialData ?? placeholderData
|
||||
const fallbackData =
|
||||
typeof fallbackDataSource === 'function'
|
||||
? (fallbackDataSource as () => T | undefined)()
|
||||
: fallbackDataSource
|
||||
const serializedKey = serializeQueryKey(queryKey)
|
||||
if (enabled && fallbackData !== undefined && !queryCache.has(serializedKey)) {
|
||||
queryCache.set(serializedKey, fallbackData)
|
||||
setCachedData(queryKey, fallbackData)
|
||||
}
|
||||
|
||||
const swr = useSWR<T>(enabled ? keyToSWRKey(queryKey) : null, queryFn, {
|
||||
const swr = useSWR<T>(enabled ? queryKey : null, queryFn, {
|
||||
dedupingInterval: staleTime,
|
||||
errorRetryCount: retry === false ? 0 : retry,
|
||||
errorRetryInterval: resolveRetryInterval(retryDelay),
|
||||
errorRetryInterval:
|
||||
typeof retryDelay === 'number'
|
||||
? retryDelay
|
||||
: swrConfig.errorRetryInterval,
|
||||
fallbackData,
|
||||
keepPreviousData: placeholderData !== undefined,
|
||||
onErrorRetry: (_error, _key, config, revalidate, { retryCount }) => {
|
||||
const maxRetries = config.errorRetryCount
|
||||
if (maxRetries !== undefined && retryCount > maxRetries) return
|
||||
|
||||
const interval =
|
||||
typeof retryDelay === 'function'
|
||||
? retryDelay(Math.max(retryCount - 1, 0))
|
||||
: config.errorRetryInterval
|
||||
|
||||
setTimeout(() => {
|
||||
revalidate({ retryCount, dedupe: true })
|
||||
}, interval)
|
||||
},
|
||||
revalidateOnFocus: refetchOnWindowFocus,
|
||||
revalidateOnReconnect: refetchOnReconnect,
|
||||
refreshInterval: refetchInterval || 0,
|
||||
refreshWhenHidden: refetchIntervalInBackground ?? false,
|
||||
onSuccess: (data) => {
|
||||
queryCache.set(serializedKey, data)
|
||||
setCachedData(queryKey, data)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -168,7 +163,7 @@ export function useQuery<T>(options: QueryOptions<T>): QueryResult<T> {
|
||||
refetch: async () => {
|
||||
const data = await swr.mutate()
|
||||
if (data !== undefined) {
|
||||
queryCache.set(serializedKey, data)
|
||||
setCachedData(queryKey, data)
|
||||
}
|
||||
return { data }
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user