From 10d32beeb119f9a9f35004c1cc24f2d785a404ad Mon Sep 17 00:00:00 2001 From: lyzno1 Date: Tue, 2 Dec 2025 15:47:05 +0800 Subject: [PATCH] refactor: wrap marketplace fetches with react query --- .../hooks/use-marketplace-all-plugins.ts | 25 ++----- .../model-provider-page/hooks.ts | 23 ++---- .../components/plugins/marketplace/hooks.ts | 73 +++++++++++++------ .../components/plugins/marketplace/utils.ts | 51 ++++++++----- 4 files changed, 99 insertions(+), 73 deletions(-) diff --git a/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts b/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts index 01790d7002..0c2154210c 100644 --- a/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts +++ b/web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts @@ -1,39 +1,28 @@ import { - useCallback, useEffect, useMemo, - useState, } from 'react' import { useMarketplacePlugins, + useMarketplacePluginsByCollectionId, } from '@/app/components/plugins/marketplace/hooks' -import type { Plugin } from '@/app/components/plugins/types' import { PluginCategoryEnum } from '@/app/components/plugins/types' -import { getMarketplacePluginsByCollectionId } from '@/app/components/plugins/marketplace/utils' export const useMarketplaceAllPlugins = (providers: any[], searchText: string) => { const exclude = useMemo(() => { return providers.map(provider => provider.plugin_id) }, [providers]) - const [collectionPlugins, setCollectionPlugins] = useState([]) - + const { + plugins: collectionPlugins = [], + isLoading: isCollectionLoading, + } = useMarketplacePluginsByCollectionId('__datasource-settings-pinned-datasources') const { plugins, queryPlugins, queryPluginsWithDebounced, - isLoading, + isLoading: isPluginsLoading, } = useMarketplacePlugins() - const getCollectionPlugins = useCallback(async () => { - const collectionPlugins = await getMarketplacePluginsByCollectionId('__datasource-settings-pinned-datasources') - - setCollectionPlugins(collectionPlugins) - }, []) - - useEffect(() => { - getCollectionPlugins() - }, [getCollectionPlugins]) - useEffect(() => { if (searchText) { queryPluginsWithDebounced({ @@ -75,6 +64,6 @@ export const useMarketplaceAllPlugins = (providers: any[], searchText: string) = return { plugins: allPlugins, - isLoading, + isLoading: isCollectionLoading || isPluginsLoading, } } diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 8cfd144681..0ffd1df9de 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -33,10 +33,9 @@ import { import { useProviderContext } from '@/context/provider-context' import { useMarketplacePlugins, + useMarketplacePluginsByCollectionId, } from '@/app/components/plugins/marketplace/hooks' -import type { Plugin } from '@/app/components/plugins/types' import { PluginCategoryEnum } from '@/app/components/plugins/types' -import { getMarketplacePluginsByCollectionId } from '@/app/components/plugins/marketplace/utils' import { useModalContextSelector } from '@/context/modal-context' import { useEventEmitterContextContext } from '@/context/event-emitter' import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card' @@ -255,25 +254,17 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: const exclude = useMemo(() => { return providers.map(provider => provider.provider.replace(/(.+)\/([^/]+)$/, '$1')) }, [providers]) - const [collectionPlugins, setCollectionPlugins] = useState([]) - + const { + plugins: collectionPlugins = [], + isLoading: isCollectionLoading, + } = useMarketplacePluginsByCollectionId('__model-settings-pinned-models') const { plugins, queryPlugins, queryPluginsWithDebounced, - isLoading, + isLoading: isPluginsLoading, } = useMarketplacePlugins() - const getCollectionPlugins = useCallback(async () => { - const collectionPlugins = await getMarketplacePluginsByCollectionId('__model-settings-pinned-models') - - setCollectionPlugins(collectionPlugins) - }, []) - - useEffect(() => { - getCollectionPlugins() - }, [getCollectionPlugins]) - useEffect(() => { if (searchText) { queryPluginsWithDebounced({ @@ -315,7 +306,7 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: return { plugins: allPlugins, - isLoading, + isLoading: isCollectionLoading || isPluginsLoading, } } diff --git a/web/app/components/plugins/marketplace/hooks.ts b/web/app/components/plugins/marketplace/hooks.ts index 5bc9263aaa..cc829a872f 100644 --- a/web/app/components/plugins/marketplace/hooks.ts +++ b/web/app/components/plugins/marketplace/hooks.ts @@ -3,6 +3,7 @@ import { useEffect, useState, } from 'react' +import { useQuery } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { useDebounceFn } from 'ahooks' import type { @@ -16,6 +17,7 @@ import type { import { getFormattedPlugin, getMarketplaceCollectionsAndPlugins, + getMarketplacePluginsByCollectionId, } from './utils' import i18n from '@/i18n-config/i18next-config' import { @@ -23,35 +25,62 @@ import { } from '@/service/use-plugins' export const useMarketplaceCollectionsAndPlugins = () => { - const [isLoading, setIsLoading] = useState(false) - const [isSuccess, setIsSuccess] = useState(false) - const [marketplaceCollections, setMarketplaceCollections] = useState() - const [marketplaceCollectionPluginsMap, setMarketplaceCollectionPluginsMap] = useState>() + const [queryParams, setQueryParams] = useState() + const [marketplaceCollectionsOverride, setMarketplaceCollections] = useState() + const [marketplaceCollectionPluginsMapOverride, setMarketplaceCollectionPluginsMap] = useState>() - const queryMarketplaceCollectionsAndPlugins = useCallback(async (query?: CollectionsAndPluginsSearchParams) => { - try { - setIsLoading(true) - setIsSuccess(false) - const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins(query) - setIsLoading(false) - setIsSuccess(true) - setMarketplaceCollections(marketplaceCollections) - setMarketplaceCollectionPluginsMap(marketplaceCollectionPluginsMap) - } - // eslint-disable-next-line unused-imports/no-unused-vars - catch (e) { - setIsLoading(false) - setIsSuccess(false) - } + const { + data, + isFetching, + isSuccess, + } = useQuery({ + queryKey: ['marketplaceCollectionsAndPlugins', queryParams], + queryFn: ({ signal }) => getMarketplaceCollectionsAndPlugins(queryParams, { signal }), + enabled: queryParams !== undefined, + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + retry: false, + }) + + const queryMarketplaceCollectionsAndPlugins = useCallback((query?: CollectionsAndPluginsSearchParams) => { + setQueryParams(query ? { ...query } : {}) }, []) return { - marketplaceCollections, + marketplaceCollections: marketplaceCollectionsOverride ?? data?.marketplaceCollections, setMarketplaceCollections, - marketplaceCollectionPluginsMap, + marketplaceCollectionPluginsMap: marketplaceCollectionPluginsMapOverride ?? data?.marketplaceCollectionPluginsMap, setMarketplaceCollectionPluginsMap, queryMarketplaceCollectionsAndPlugins, - isLoading, + isLoading: isFetching, + isSuccess, + } +} + +export const useMarketplacePluginsByCollectionId = ( + collectionId?: string, + query?: CollectionsAndPluginsSearchParams, +) => { + const { + data, + isFetching, + isSuccess, + } = useQuery({ + queryKey: ['marketplaceCollectionPlugins', collectionId, query], + queryFn: ({ signal }) => { + if (!collectionId) + return Promise.resolve([]) + return getMarketplacePluginsByCollectionId(collectionId, query, { signal }) + }, + enabled: !!collectionId, + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + retry: false, + }) + + return { + plugins: data || [], + isLoading: isFetching, isSuccess, } } diff --git a/web/app/components/plugins/marketplace/utils.ts b/web/app/components/plugins/marketplace/utils.ts index f424811537..e12cadf070 100644 --- a/web/app/components/plugins/marketplace/utils.ts +++ b/web/app/components/plugins/marketplace/utils.ts @@ -13,6 +13,14 @@ import { } from '@/config' import { getMarketplaceUrl } from '@/utils/var' +type MarketplaceFetchOptions = { + signal?: AbortSignal +} + +const getMarketplaceHeaders = () => new Headers({ + 'X-Dify-Version': !IS_MARKETPLACE ? APP_VERSION : '999.0.0', +}) + export const getPluginIconInMarketplace = (plugin: Plugin) => { if (plugin.type === 'bundle') return `${MARKETPLACE_API_PREFIX}/bundles/${plugin.org}/${plugin.name}/icon` @@ -46,20 +54,23 @@ export const getPluginDetailLinkInMarketplace = (plugin: Plugin) => { return `/plugins/${plugin.org}/${plugin.name}` } -export const getMarketplacePluginsByCollectionId = async (collectionId: string, query?: CollectionsAndPluginsSearchParams) => { - let plugins: Plugin[] +export const getMarketplacePluginsByCollectionId = async ( + collectionId: string, + query?: CollectionsAndPluginsSearchParams, + options?: MarketplaceFetchOptions, +) => { + let plugins: Plugin[] = [] try { const url = `${MARKETPLACE_API_PREFIX}/collections/${collectionId}/plugins` - const headers = new Headers({ - 'X-Dify-Version': !IS_MARKETPLACE ? APP_VERSION : '999.0.0', - }) + const headers = getMarketplaceHeaders() const marketplaceCollectionPluginsData = await globalThis.fetch( url, { cache: 'no-store', method: 'POST', headers, + signal: options?.signal, body: JSON.stringify({ category: query?.category, exclude: query?.exclude, @@ -68,9 +79,7 @@ export const getMarketplacePluginsByCollectionId = async (collectionId: string, }, ) const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json() - plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => { - return getFormattedPlugin(plugin) - }) + plugins = (marketplaceCollectionPluginsDataJson.data.plugins || []).map((plugin: Plugin) => getFormattedPlugin(plugin)) } // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { @@ -80,23 +89,31 @@ export const getMarketplacePluginsByCollectionId = async (collectionId: string, return plugins } -export const getMarketplaceCollectionsAndPlugins = async (query?: CollectionsAndPluginsSearchParams) => { - let marketplaceCollections = [] as MarketplaceCollection[] - let marketplaceCollectionPluginsMap = {} as Record +export const getMarketplaceCollectionsAndPlugins = async ( + query?: CollectionsAndPluginsSearchParams, + options?: MarketplaceFetchOptions, +) => { + let marketplaceCollections: MarketplaceCollection[] = [] + let marketplaceCollectionPluginsMap: Record = {} try { let marketplaceUrl = `${MARKETPLACE_API_PREFIX}/collections?page=1&page_size=100` if (query?.condition) marketplaceUrl += `&condition=${query.condition}` if (query?.type) marketplaceUrl += `&type=${query.type}` - const headers = new Headers({ - 'X-Dify-Version': !IS_MARKETPLACE ? APP_VERSION : '999.0.0', - }) - const marketplaceCollectionsData = await globalThis.fetch(marketplaceUrl, { headers, cache: 'no-store' }) + const headers = getMarketplaceHeaders() + const marketplaceCollectionsData = await globalThis.fetch( + marketplaceUrl, + { + headers, + cache: 'no-store', + signal: options?.signal, + }, + ) const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json() - marketplaceCollections = marketplaceCollectionsDataJson.data.collections + marketplaceCollections = marketplaceCollectionsDataJson.data.collections || [] await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => { - const plugins = await getMarketplacePluginsByCollectionId(collection.name, query) + const plugins = await getMarketplacePluginsByCollectionId(collection.name, query, options) marketplaceCollectionPluginsMap[collection.name] = plugins }))