refactor: replace useSWR with custom hooks for update and network interfaces (#6195)

This commit is contained in:
Tunglies
2026-01-27 20:52:20 +08:00
committed by GitHub
parent 36926df26c
commit c57a962109
8 changed files with 97 additions and 69 deletions

View File

@@ -10,14 +10,13 @@ import { useLockFn } from "ahooks";
import { useCallback, useEffect, useMemo, useReducer } from "react"; import { useCallback, useEffect, useMemo, useReducer } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import useSWR from "swr";
import { useServiceInstaller } from "@/hooks/use-service-installer"; import { useServiceInstaller } from "@/hooks/use-service-installer";
import { useSystemState } from "@/hooks/use-system-state"; import { useSystemState } from "@/hooks/use-system-state";
import { useUpdate } from "@/hooks/use-update";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { getSystemInfo } from "@/services/cmds"; import { getSystemInfo } from "@/services/cmds";
import { showNotice } from "@/services/notice-service"; import { showNotice } from "@/services/notice-service";
import { checkUpdateSafe as checkUpdate } from "@/services/update";
import { version as appVersion } from "@root/package.json"; import { version as appVersion } from "@root/package.json";
import { EnhancedCard } from "./enhanced-card"; import { EnhancedCard } from "./enhanced-card";
@@ -52,6 +51,18 @@ export const SystemInfoCard = () => {
const { isAdminMode, isSidecarMode } = useSystemState(); const { isAdminMode, isSidecarMode } = useSystemState();
const { installServiceAndRestartCore } = useServiceInstaller(); const { installServiceAndRestartCore } = useServiceInstaller();
// 自动检查更新逻辑
const { checkUpdate: triggerCheckUpdate } = useUpdate(true, {
onSuccess: () => {
const now = Date.now();
localStorage.setItem("last_check_update", now.toString());
dispatchSystemState({
type: "set-last-check-update",
payload: new Date(now).toLocaleString(),
});
},
});
// 系统信息状态 // 系统信息状态
const [systemState, dispatchSystemState] = useReducer(systemStateReducer, { const [systemState, dispatchSystemState] = useReducer(systemStateReducer, {
osInfo: "", osInfo: "",
@@ -109,7 +120,7 @@ export const SystemInfoCard = () => {
timeoutId = window.setTimeout(() => { timeoutId = window.setTimeout(() => {
if (verge?.auto_check_update) { if (verge?.auto_check_update) {
checkUpdate().catch(console.error); triggerCheckUpdate().catch(console.error);
} }
}, 5000); }, 5000);
} }
@@ -118,26 +129,7 @@ export const SystemInfoCard = () => {
window.clearTimeout(timeoutId); window.clearTimeout(timeoutId);
} }
}; };
}, [verge?.auto_check_update, dispatchSystemState]); }, [verge?.auto_check_update, dispatchSystemState, triggerCheckUpdate]);
// 自动检查更新逻辑
useSWR(
verge?.auto_check_update ? "checkUpdate" : null,
async () => {
const now = Date.now();
localStorage.setItem("last_check_update", now.toString());
dispatchSystemState({
type: "set-last-check-update",
payload: new Date(now).toLocaleString(),
});
return await checkUpdate();
},
{
revalidateOnFocus: false,
refreshInterval: 24 * 60 * 60 * 1000, // 每天检查一次
dedupingInterval: 60 * 60 * 1000, // 1小时内不重复检查
},
);
// 导航到设置页面 // 导航到设置页面
const goToSettings = useCallback(() => { const goToSettings = useCallback(() => {
@@ -164,7 +156,7 @@ export const SystemInfoCard = () => {
// 检查更新 // 检查更新
const onCheckUpdate = useLockFn(async () => { const onCheckUpdate = useLockFn(async () => {
try { try {
const info = await checkUpdate(); const info = await triggerCheckUpdate();
if (!info?.available) { if (!info?.available) {
showNotice.success( showNotice.success(
"settings.components.verge.advanced.notifications.latestVersion", "settings.components.verge.advanced.notifications.latestVersion",

View File

@@ -1,10 +1,8 @@
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import { useRef } from "react"; import { useRef } from "react";
import useSWR from "swr";
import { DialogRef } from "@/components/base"; import { DialogRef } from "@/components/base";
import { useVerge } from "@/hooks/use-verge"; import { useUpdate } from "@/hooks/use-update";
import { checkUpdateSafe } from "@/services/update";
import { UpdateViewer } from "../setting/mods/update-viewer"; import { UpdateViewer } from "../setting/mods/update-viewer";
@@ -14,20 +12,9 @@ interface Props {
export const UpdateButton = (props: Props) => { export const UpdateButton = (props: Props) => {
const { className } = props; const { className } = props;
const { verge } = useVerge();
const { auto_check_update } = verge || {};
const viewerRef = useRef<DialogRef>(null); const viewerRef = useRef<DialogRef>(null);
const { data: updateInfo } = useSWR( const { updateInfo } = useUpdate();
auto_check_update || auto_check_update === null ? "checkUpdate" : null,
checkUpdateSafe,
{
errorRetryCount: 2,
revalidateIfStale: false,
focusThrottleInterval: 36e5, // 1 hour
},
);
if (!updateInfo?.available) return null; if (!updateInfo?.available) return null;

View File

@@ -1,9 +1,8 @@
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import useSWR from "swr";
import { useRuntimeConfig } from "@/hooks/use-clash";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { useAppData } from "@/providers/app-data-context"; import { useAppData } from "@/providers/app-data-context";
import { getRuntimeConfig } from "@/services/cmds";
import delayManager from "@/services/delay"; import delayManager from "@/services/delay";
import { debugLog } from "@/utils/debug"; import { debugLog } from "@/utils/debug";
@@ -107,14 +106,7 @@ export const useRenderList = (
const latencyTimeout = verge?.default_latency_timeout; const latencyTimeout = verge?.default_latency_timeout;
// 获取运行时配置用于链式代理模式 // 获取运行时配置用于链式代理模式
const { data: runtimeConfig } = useSWR( const { data: runtimeConfig } = useRuntimeConfig(!!isChainMode);
isChainMode ? "getRuntimeConfig" : null,
getRuntimeConfig,
{
revalidateOnFocus: false,
revalidateIfStale: true,
},
);
// 计算列数 // 计算列数
const col = useMemo( const col = useMemo(

View File

@@ -4,10 +4,9 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager";
import type { Ref } from "react"; import type { Ref } from "react";
import { useImperativeHandle, useState } from "react"; import { useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import useSWR from "swr";
import { BaseDialog, DialogRef } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { getNetworkInterfacesInfo } from "@/services/cmds"; import { useNetworkInterfaces } from "@/hooks/use-network";
import { showNotice } from "@/services/notice-service"; import { showNotice } from "@/services/notice-service";
export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) { export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
@@ -22,13 +21,7 @@ export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
close: () => setOpen(false), close: () => setOpen(false),
})); }));
const { data: networkInterfaces } = useSWR( const { networkInterfaces } = useNetworkInterfaces();
"clash-verge-rev-internal://network-interfaces",
getNetworkInterfacesInfo,
{
fallbackData: [], // default data before fetch
},
);
return ( return (
<BaseDialog <BaseDialog

View File

@@ -8,13 +8,12 @@ import { useImperativeHandle, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw"; import rehypeRaw from "rehype-raw";
import useSWR from "swr";
import { BaseDialog, DialogRef } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { useUpdate } from "@/hooks/use-update";
import { portableFlag } from "@/pages/_layout"; import { portableFlag } from "@/pages/_layout";
import { showNotice } from "@/services/notice-service"; import { showNotice } from "@/services/notice-service";
import { useSetUpdateState, useUpdateState } from "@/services/states"; import { useSetUpdateState, useUpdateState } from "@/services/states";
import { checkUpdateSafe as checkUpdate } from "@/services/update";
export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) { export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -23,11 +22,7 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
const updateState = useUpdateState(); const updateState = useUpdateState();
const setUpdateState = useSetUpdateState(); const setUpdateState = useSetUpdateState();
const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, { const { updateInfo } = useUpdate();
errorRetryCount: 2,
revalidateIfStale: false,
focusThrottleInterval: 36e5, // 1 hour
});
const [downloaded, setDownloaded] = useState(0); const [downloaded, setDownloaded] = useState(0);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);

View File

@@ -51,11 +51,12 @@ const validatePorts = (patch: ClashInfoPatch) => {
}); });
}; };
export const useRuntimeConfig = (shouldFetch: boolean = true) => {
return useSWR(shouldFetch ? "getRuntimeConfig" : null, getRuntimeConfig);
};
export const useClash = () => { export const useClash = () => {
const { data: clash, mutate: mutateClash } = useSWR( const { data: clash, mutate: mutateClash } = useRuntimeConfig();
"getRuntimeConfig",
getRuntimeConfig,
);
const { data: versionData, mutate: mutateVersion } = useSWR( const { data: versionData, mutate: mutateVersion } = useSWR(
"getVersion", "getVersion",

22
src/hooks/use-network.ts Normal file
View File

@@ -0,0 +1,22 @@
import useSWR from "swr";
import { getNetworkInterfacesInfo } from "@/services/cmds";
export const useNetworkInterfaces = () => {
const { data, error, isLoading, mutate } = useSWR(
"getNetworkInterfacesInfo",
getNetworkInterfacesInfo,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
fallbackData: [],
},
);
return {
networkInterfaces: data || [],
loading: isLoading,
error,
mutate,
};
};

46
src/hooks/use-update.ts Normal file
View File

@@ -0,0 +1,46 @@
import useSWR, { SWRConfiguration } from "swr";
import { checkUpdateSafe } from "@/services/update";
import { useVerge } from "./use-verge";
export interface UpdateInfo {
version: string;
body: string;
date: string;
available: boolean;
downloadAndInstall: (onEvent?: any) => Promise<void>;
}
export const useUpdate = (
enabled: boolean = true,
options?: SWRConfiguration,
) => {
const { verge } = useVerge();
const { auto_check_update } = verge || {};
// Determine if we should check for updates
// If enabled is explicitly false, don't check
// Otherwise, respect the auto_check_update setting (or default to true if null/undefined for manual triggers)
const shouldCheck = enabled && auto_check_update !== false;
const {
data: updateInfo,
mutate: checkUpdate,
isValidating,
} = useSWR(shouldCheck ? "checkUpdate" : null, checkUpdateSafe, {
errorRetryCount: 2,
revalidateIfStale: false,
revalidateOnFocus: false,
focusThrottleInterval: 36e5, // 1 hour
refreshInterval: 24 * 60 * 60 * 1000, // 24 hours
dedupingInterval: 60 * 60 * 1000, // 1 hour
...options,
});
return {
updateInfo,
checkUpdate,
loading: isValidating,
};
};