Files
clash-verge-rev/src/hooks/use-profiles.ts
T
Tunglies 4ed474706a refactor(frontend): consolidate query state ownership
Consolidate ordinary query snapshots on SWR and make Profiles own its query key, cache mutation, and invalidation policy.

Impact:
- remove the generic synchronous query Map while keeping dedicated Mihomo WebSocket snapshots
- eliminate duplicate Profiles refreshes while preserving retries and the 800 ms event throttle
- preserve preload-backed Verge updates and system proxy rollback behavior
- scope uptime polling to the Home Clash info card instead of the app lifetime
- leave RunState, Connections, Traffic, backend, and IPC contracts unchanged
- reduce frontend code by 72 net lines without adding dependencies
2026-09-06 04:32:22 +08:00

93 lines
2.3 KiB
TypeScript

import { useCallback, useRef } from 'react'
import { getProfiles, patchProfile, patchProfilesConfig } from '@/services/cmds'
import {
fetchCacheData,
revalidateQuery,
setCacheData,
useQuery,
} from '@/services/query-client'
import { debugLog } from '@/utils/debug'
const profilesQueryKey = ['getProfiles'] as const
export const revalidateProfiles = () => revalidateQuery(profilesQueryKey)
export const fetchProfilesIntoCache = () =>
fetchCacheData(profilesQueryKey, getProfiles)
export const useProfiles = () => {
const {
data: profiles,
refetch,
error,
isFetching: isValidating,
} = useQuery({
queryKey: profilesQueryKey,
queryFn: async () => {
const data = await getProfiles()
debugLog(
'[useProfiles] 配置数据更新成功,配置数量:',
data?.items?.length || 0,
)
return data
},
refetchOnWindowFocus: false,
refetchOnReconnect: false,
staleTime: 500,
retry: 3,
retryDelay: 1000,
refetchInterval: false,
})
const refetchRef = useRef(refetch)
refetchRef.current = refetch
const mutateProfiles = useCallback(async () => {
await refetchRef.current()
}, [])
const patchProfiles = useCallback(
async (value: Partial<IProfilesConfig>) => {
try {
const outcome = await patchProfilesConfig(value)
if (outcome.status === 'valid') {
await setCacheData<IProfilesConfig>(profilesQueryKey, (current) =>
current ? { ...current, ...value } : current,
)
} else if (outcome.status !== 'busy') {
await mutateProfiles()
}
return outcome
} catch (error) {
await mutateProfiles()
throw error
}
},
[mutateProfiles],
)
const patchCurrent = useCallback(
async (value: Partial<IProfileItem>) => {
if (profiles?.current) {
await patchProfile(profiles.current, value)
void mutateProfiles()
}
},
[mutateProfiles, profiles],
)
return {
profiles,
current: profiles?.items?.find((p) => p && p.uid === profiles.current),
patchProfiles,
patchCurrent,
mutateProfiles,
// 新增故障检测状态
isLoading: isValidating,
error,
isStale: !profiles && !error && !isValidating, // 检测是否处于异常状态
}
}