mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-02-10 19:50:28 +08:00
refactor: extract common config context logic into factory function
This commit is contained in:
parent
38389e0c3c
commit
2c638f56c0
72
src/renderer/src/hooks/create-config-context.tsx
Normal file
72
src/renderer/src/hooks/create-config-context.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React, { createContext, useContext, ReactNode, useCallback, useEffect } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { showError } from '@renderer/utils/error-display'
|
||||||
|
import useSWR, { KeyedMutator } from 'swr'
|
||||||
|
|
||||||
|
interface ConfigContextValue<T> {
|
||||||
|
config: T | undefined
|
||||||
|
mutate: KeyedMutator<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateConfigContextOptions<T> {
|
||||||
|
swrKey: string
|
||||||
|
fetcher: () => Promise<T>
|
||||||
|
ipcEvent: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createConfigContext<T>(options: CreateConfigContextOptions<T>) {
|
||||||
|
const { swrKey, fetcher, ipcEvent } = options
|
||||||
|
const Context = createContext<ConfigContextValue<T> | undefined>(undefined)
|
||||||
|
|
||||||
|
const Provider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { data: config, mutate } = useSWR(swrKey, fetcher)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (): void => {
|
||||||
|
mutate()
|
||||||
|
}
|
||||||
|
window.electron.ipcRenderer.on(ipcEvent, handler)
|
||||||
|
return () => {
|
||||||
|
window.electron.ipcRenderer.removeListener(ipcEvent, handler)
|
||||||
|
}
|
||||||
|
}, [mutate])
|
||||||
|
|
||||||
|
return <Context.Provider value={{ config, mutate }}>{children}</Context.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
const useConfig = (): ConfigContextValue<T> => {
|
||||||
|
const context = useContext(Context)
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(`useConfig must be used within Provider`)
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
return { Provider, useConfig, Context }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActionOptions {
|
||||||
|
errorKey: string
|
||||||
|
updateTray?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useConfigAction<T>(
|
||||||
|
mutate: KeyedMutator<T>,
|
||||||
|
action: () => Promise<void>,
|
||||||
|
options: ActionOptions
|
||||||
|
): () => Promise<void> {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await action()
|
||||||
|
} catch (e) {
|
||||||
|
await showError(e, t(options.errorKey))
|
||||||
|
} finally {
|
||||||
|
mutate()
|
||||||
|
if (options.updateTray) {
|
||||||
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [mutate, action, t, options.errorKey, options.updateTray])
|
||||||
|
}
|
||||||
@ -1,51 +1,60 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { ReactNode, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import { createConfigContext } from './create-config-context'
|
||||||
import { getAppConfig, patchAppConfig as patch } from '@renderer/utils/ipc'
|
import { getAppConfig, patchAppConfig as patch } from '@renderer/utils/ipc'
|
||||||
|
|
||||||
|
const { Provider, useConfig } = createConfigContext<IAppConfig>({
|
||||||
|
swrKey: 'getAppConfig',
|
||||||
|
fetcher: getAppConfig,
|
||||||
|
ipcEvent: 'appConfigUpdated'
|
||||||
|
})
|
||||||
|
|
||||||
interface AppConfigContextType {
|
interface AppConfigContextType {
|
||||||
appConfig: IAppConfig | undefined
|
appConfig: IAppConfig | undefined
|
||||||
mutateAppConfig: () => void
|
mutateAppConfig: () => void
|
||||||
patchAppConfig: (value: Partial<IAppConfig>) => Promise<void>
|
patchAppConfig: (value: Partial<IAppConfig>) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppConfigContext = createContext<AppConfigContextType | undefined>(undefined)
|
|
||||||
|
|
||||||
export const AppConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const AppConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<Provider>
|
||||||
|
<AppConfigContextWrapper>{children}</AppConfigContextWrapper>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppConfigContextWrapper: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { config, mutate } = useConfig()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { data: appConfig, mutate: mutateAppConfig } = useSWR('getConfig', () => getAppConfig())
|
|
||||||
|
|
||||||
const patchAppConfig = async (value: Partial<IAppConfig>): Promise<void> => {
|
const patchAppConfig = useCallback(
|
||||||
try {
|
async (value: Partial<IAppConfig>): Promise<void> => {
|
||||||
await patch(value)
|
try {
|
||||||
} catch (e) {
|
await patch(value)
|
||||||
await showError(e, t('common.error.updateAppConfigFailed'))
|
} catch (e) {
|
||||||
} finally {
|
await showError(e, t('common.error.updateAppConfigFailed'))
|
||||||
mutateAppConfig()
|
} finally {
|
||||||
}
|
mutate()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
React.useEffect(() => {
|
[mutate, t]
|
||||||
const handler = (): void => {
|
)
|
||||||
mutateAppConfig()
|
|
||||||
}
|
|
||||||
window.electron.ipcRenderer.on('appConfigUpdated', handler)
|
|
||||||
return (): void => {
|
|
||||||
window.electron.ipcRenderer.removeListener('appConfigUpdated', handler)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppConfigContext.Provider value={{ appConfig, mutateAppConfig, patchAppConfig }}>
|
<AppConfigContext.Provider
|
||||||
|
value={{ appConfig: config, mutateAppConfig: mutate, patchAppConfig }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</AppConfigContext.Provider>
|
</AppConfigContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AppConfigContext = React.createContext<AppConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const useAppConfig = (): AppConfigContextType => {
|
export const useAppConfig = (): AppConfigContextType => {
|
||||||
const context = useContext(AppConfigContext)
|
const context = React.useContext(AppConfigContext)
|
||||||
if (context === undefined) {
|
if (!context) {
|
||||||
throw new Error('useAppConfig must be used within an AppConfigProvider')
|
throw new Error('useAppConfig must be used within an AppConfigProvider')
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { createContext, useContext, ReactNode } from 'react'
|
import React, { ReactNode, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import { createConfigContext } from './create-config-context'
|
||||||
import {
|
import {
|
||||||
getOverrideConfig,
|
getOverrideConfig,
|
||||||
setOverrideConfig as set,
|
setOverrideConfig as set,
|
||||||
@ -10,6 +10,12 @@ import {
|
|||||||
updateOverrideItem as update
|
updateOverrideItem as update
|
||||||
} from '@renderer/utils/ipc'
|
} from '@renderer/utils/ipc'
|
||||||
|
|
||||||
|
const { Provider, useConfig } = createConfigContext<IOverrideConfig>({
|
||||||
|
swrKey: 'getOverrideConfig',
|
||||||
|
fetcher: getOverrideConfig,
|
||||||
|
ipcEvent: 'overrideConfigUpdated'
|
||||||
|
})
|
||||||
|
|
||||||
interface OverrideConfigContextType {
|
interface OverrideConfigContextType {
|
||||||
overrideConfig: IOverrideConfig | undefined
|
overrideConfig: IOverrideConfig | undefined
|
||||||
setOverrideConfig: (config: IOverrideConfig) => Promise<void>
|
setOverrideConfig: (config: IOverrideConfig) => Promise<void>
|
||||||
@ -19,60 +25,59 @@ interface OverrideConfigContextType {
|
|||||||
removeOverrideItem: (id: string) => Promise<void>
|
removeOverrideItem: (id: string) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const OverrideConfigContext = createContext<OverrideConfigContextType | undefined>(undefined)
|
const OverrideConfigContext = React.createContext<OverrideConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<Provider>
|
||||||
|
<OverrideConfigContextWrapper>{children}</OverrideConfigContextWrapper>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const OverrideConfigContextWrapper: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const { config, mutate } = useConfig()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { data: overrideConfig, mutate: mutateOverrideConfig } = useSWR('getOverrideConfig', () =>
|
|
||||||
getOverrideConfig()
|
const withErrorHandling = useCallback(
|
||||||
|
(action: () => Promise<void>, errorKey: string) => async () => {
|
||||||
|
try {
|
||||||
|
await action()
|
||||||
|
} catch (e) {
|
||||||
|
await showError(e, t(errorKey))
|
||||||
|
} finally {
|
||||||
|
mutate()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[mutate, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
const setOverrideConfig = async (config: IOverrideConfig): Promise<void> => {
|
const setOverrideConfig = useCallback(
|
||||||
try {
|
(cfg: IOverrideConfig) => withErrorHandling(() => set(cfg), 'common.error.saveOverrideConfigFailed')(),
|
||||||
await set(config)
|
[withErrorHandling]
|
||||||
} catch (e) {
|
)
|
||||||
await showError(e, t('common.error.saveOverrideConfigFailed'))
|
|
||||||
} finally {
|
|
||||||
mutateOverrideConfig()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addOverrideItem = async (item: Partial<IOverrideItem>): Promise<void> => {
|
const addOverrideItem = useCallback(
|
||||||
try {
|
(item: Partial<IOverrideItem>) => withErrorHandling(() => add(item), 'common.error.addOverrideFailed')(),
|
||||||
await add(item)
|
[withErrorHandling]
|
||||||
} catch (e) {
|
)
|
||||||
await showError(e, t('common.error.addOverrideFailed'))
|
|
||||||
} finally {
|
|
||||||
mutateOverrideConfig()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeOverrideItem = async (id: string): Promise<void> => {
|
const removeOverrideItem = useCallback(
|
||||||
try {
|
(id: string) => withErrorHandling(() => remove(id), 'common.error.deleteOverrideFailed')(),
|
||||||
await remove(id)
|
[withErrorHandling]
|
||||||
} catch (e) {
|
)
|
||||||
await showError(e, t('common.error.deleteOverrideFailed'))
|
|
||||||
} finally {
|
|
||||||
mutateOverrideConfig()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateOverrideItem = async (item: IOverrideItem): Promise<void> => {
|
const updateOverrideItem = useCallback(
|
||||||
try {
|
(item: IOverrideItem) => withErrorHandling(() => update(item), 'common.error.updateOverrideFailed')(),
|
||||||
await update(item)
|
[withErrorHandling]
|
||||||
} catch (e) {
|
)
|
||||||
await showError(e, t('common.error.updateOverrideFailed'))
|
|
||||||
} finally {
|
|
||||||
mutateOverrideConfig()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OverrideConfigContext.Provider
|
<OverrideConfigContext.Provider
|
||||||
value={{
|
value={{
|
||||||
overrideConfig,
|
overrideConfig: config,
|
||||||
setOverrideConfig,
|
setOverrideConfig,
|
||||||
mutateOverrideConfig,
|
mutateOverrideConfig: mutate,
|
||||||
addOverrideItem,
|
addOverrideItem,
|
||||||
removeOverrideItem,
|
removeOverrideItem,
|
||||||
updateOverrideItem
|
updateOverrideItem
|
||||||
@ -84,8 +89,8 @@ export const OverrideConfigProvider: React.FC<{ children: ReactNode }> = ({ chil
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useOverrideConfig = (): OverrideConfigContextType => {
|
export const useOverrideConfig = (): OverrideConfigContextType => {
|
||||||
const context = useContext(OverrideConfigContext)
|
const context = React.useContext(OverrideConfigContext)
|
||||||
if (context === undefined) {
|
if (!context) {
|
||||||
throw new Error('useOverrideConfig must be used within an OverrideConfigProvider')
|
throw new Error('useOverrideConfig must be used within an OverrideConfigProvider')
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { createContext, ReactNode, useContext } from 'react'
|
import React, { ReactNode, useCallback, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { showError } from '@renderer/utils/error-display'
|
import { showError } from '@renderer/utils/error-display'
|
||||||
import useSWR from 'swr'
|
import { createConfigContext } from './create-config-context'
|
||||||
import {
|
import {
|
||||||
addProfileItem as add,
|
addProfileItem as add,
|
||||||
changeCurrentProfile as change,
|
changeCurrentProfile as change,
|
||||||
@ -11,6 +11,12 @@ import {
|
|||||||
updateProfileItem as update
|
updateProfileItem as update
|
||||||
} from '@renderer/utils/ipc'
|
} from '@renderer/utils/ipc'
|
||||||
|
|
||||||
|
const { Provider, useConfig } = createConfigContext<IProfileConfig>({
|
||||||
|
swrKey: 'getProfileConfig',
|
||||||
|
fetcher: getProfileConfig,
|
||||||
|
ipcEvent: 'profileConfigUpdated'
|
||||||
|
})
|
||||||
|
|
||||||
interface ProfileConfigContextType {
|
interface ProfileConfigContextType {
|
||||||
profileConfig: IProfileConfig | undefined
|
profileConfig: IProfileConfig | undefined
|
||||||
setProfileConfig: (config: IProfileConfig) => Promise<void>
|
setProfileConfig: (config: IProfileConfig) => Promise<void>
|
||||||
@ -21,80 +27,64 @@ interface ProfileConfigContextType {
|
|||||||
changeCurrentProfile: (id: string) => Promise<void>
|
changeCurrentProfile: (id: string) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProfileConfigContext = createContext<ProfileConfigContextType | undefined>(undefined)
|
const ProfileConfigContext = React.createContext<ProfileConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const { t } = useTranslation()
|
return (
|
||||||
const { data: profileConfig, mutate: mutateProfileConfig } = useSWR('getProfileConfig', () =>
|
<Provider>
|
||||||
getProfileConfig()
|
<ProfileConfigContextWrapper>{children}</ProfileConfigContextWrapper>
|
||||||
|
</Provider>
|
||||||
)
|
)
|
||||||
const targetProfileId = React.useRef<string | null>(null)
|
}
|
||||||
const pendingTask = React.useRef<Promise<void> | null>(null)
|
|
||||||
|
|
||||||
const setProfileConfig = async (config: IProfileConfig): Promise<void> => {
|
const ProfileConfigContextWrapper: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
try {
|
const { config, mutate } = useConfig()
|
||||||
await set(config)
|
const { t } = useTranslation()
|
||||||
} catch (e) {
|
const targetProfileId = useRef<string | null>(null)
|
||||||
await showError(e, t('common.error.saveProfileConfigFailed'))
|
const pendingTask = useRef<Promise<void> | null>(null)
|
||||||
} finally {
|
|
||||||
mutateProfileConfig()
|
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addProfileItem = async (item: Partial<IProfileItem>): Promise<void> => {
|
const withErrorHandling = useCallback(
|
||||||
try {
|
(action: () => Promise<void>, errorKey: string, updateTray = true) =>
|
||||||
await add(item)
|
async () => {
|
||||||
} catch (e) {
|
try {
|
||||||
await showError(e, t('common.error.addProfileFailed'))
|
await action()
|
||||||
} finally {
|
} catch (e) {
|
||||||
mutateProfileConfig()
|
await showError(e, t(errorKey))
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
} finally {
|
||||||
}
|
mutate()
|
||||||
}
|
if (updateTray) {
|
||||||
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[mutate, t]
|
||||||
|
)
|
||||||
|
|
||||||
const removeProfileItem = async (id: string): Promise<void> => {
|
const setProfileConfig = useCallback(
|
||||||
try {
|
(cfg: IProfileConfig) =>
|
||||||
await remove(id)
|
withErrorHandling(() => set(cfg), 'common.error.saveProfileConfigFailed')(),
|
||||||
} catch (e) {
|
[withErrorHandling]
|
||||||
await showError(e, t('common.error.deleteProfileFailed'))
|
)
|
||||||
} finally {
|
|
||||||
mutateProfileConfig()
|
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateProfileItem = async (item: IProfileItem): Promise<void> => {
|
const addProfileItem = useCallback(
|
||||||
try {
|
(item: Partial<IProfileItem>) =>
|
||||||
await update(item)
|
withErrorHandling(() => add(item), 'common.error.addProfileFailed')(),
|
||||||
} catch (e) {
|
[withErrorHandling]
|
||||||
await showError(e, t('common.error.updateProfileFailed'))
|
)
|
||||||
} finally {
|
|
||||||
mutateProfileConfig()
|
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeCurrentProfile = async (id: string): Promise<void> => {
|
const removeProfileItem = useCallback(
|
||||||
if (targetProfileId.current === id) {
|
(id: string) => withErrorHandling(() => remove(id), 'common.error.deleteProfileFailed')(),
|
||||||
return
|
[withErrorHandling]
|
||||||
}
|
)
|
||||||
|
|
||||||
// 立即更新 UI 状态和托盘菜单,提供即时反馈
|
const updateProfileItem = useCallback(
|
||||||
if (profileConfig) {
|
(item: IProfileItem) =>
|
||||||
const optimisticUpdate = { ...profileConfig, current: id }
|
withErrorHandling(() => update(item), 'common.error.updateProfileFailed')(),
|
||||||
mutateProfileConfig(optimisticUpdate, false)
|
[withErrorHandling]
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
)
|
||||||
}
|
|
||||||
|
|
||||||
targetProfileId.current = id
|
const processChange = useCallback(async () => {
|
||||||
await processChange()
|
if (pendingTask.current) return
|
||||||
}
|
|
||||||
|
|
||||||
const processChange = async () => {
|
|
||||||
if (pendingTask.current) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
while (targetProfileId.current) {
|
while (targetProfileId.current) {
|
||||||
const targetId = targetProfileId.current
|
const targetId = targetProfileId.current
|
||||||
@ -102,41 +92,48 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
|
|
||||||
pendingTask.current = change(targetId)
|
pendingTask.current = change(targetId)
|
||||||
try {
|
try {
|
||||||
// 异步执行后台切换,不阻塞 UI
|
|
||||||
await pendingTask.current
|
await pendingTask.current
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMsg = (e as { message?: string })?.message || String(e)
|
const errorMsg = (e as { message?: string })?.message || String(e)
|
||||||
// 处理 IPC 超时错误
|
|
||||||
if (errorMsg.includes('reply was never sent')) {
|
if (errorMsg.includes('reply was never sent')) {
|
||||||
setTimeout(() => mutateProfileConfig(), 1000)
|
setTimeout(() => mutate(), 1000)
|
||||||
} else {
|
} else {
|
||||||
await showError(errorMsg, t('common.error.switchProfileFailed'))
|
await showError(errorMsg, t('common.error.switchProfileFailed'))
|
||||||
mutateProfileConfig()
|
mutate()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
pendingTask.current = null
|
pendingTask.current = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, [mutate, t])
|
||||||
|
|
||||||
|
const changeCurrentProfile = useCallback(
|
||||||
|
async (id: string) => {
|
||||||
|
if (targetProfileId.current === id) return
|
||||||
|
|
||||||
|
if (config) {
|
||||||
|
mutate({ ...config, current: id }, false)
|
||||||
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
|
}
|
||||||
|
|
||||||
|
targetProfileId.current = id
|
||||||
|
await processChange()
|
||||||
|
},
|
||||||
|
[config, mutate, processChange]
|
||||||
|
)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handler = (): void => {
|
return () => {
|
||||||
mutateProfileConfig()
|
|
||||||
}
|
|
||||||
window.electron.ipcRenderer.on('profileConfigUpdated', handler)
|
|
||||||
return (): void => {
|
|
||||||
// 清理待处理任务,防止内存泄漏
|
|
||||||
targetProfileId.current = null
|
targetProfileId.current = null
|
||||||
window.electron.ipcRenderer.removeListener('profileConfigUpdated', handler)
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfileConfigContext.Provider
|
<ProfileConfigContext.Provider
|
||||||
value={{
|
value={{
|
||||||
profileConfig,
|
profileConfig: config,
|
||||||
setProfileConfig,
|
setProfileConfig,
|
||||||
mutateProfileConfig,
|
mutateProfileConfig: mutate,
|
||||||
addProfileItem,
|
addProfileItem,
|
||||||
removeProfileItem,
|
removeProfileItem,
|
||||||
updateProfileItem,
|
updateProfileItem,
|
||||||
@ -149,8 +146,8 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useProfileConfig = (): ProfileConfigContextType => {
|
export const useProfileConfig = (): ProfileConfigContextType => {
|
||||||
const context = useContext(ProfileConfigContext)
|
const context = React.useContext(ProfileConfigContext)
|
||||||
if (context === undefined) {
|
if (!context) {
|
||||||
throw new Error('useProfileConfig must be used within a ProfileConfigProvider')
|
throw new Error('useProfileConfig must be used within a ProfileConfigProvider')
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user