mirror of https://github.com/langgenius/dify.git
refactor(web): drop swr remnants
This commit is contained in:
parent
c4336dc560
commit
8875c7f646
|
|
@ -187,7 +187,7 @@ const Template = useMemo(() => {
|
|||
|
||||
**When**: Component directly handles API calls, data transformation, or complex async operations.
|
||||
|
||||
**Dify Convention**: Use `@tanstack/react-query` hooks from `web/service/use-*.ts` or create custom data hooks. Project is migrating from SWR to React Query.
|
||||
**Dify Convention**: Use `@tanstack/react-query` hooks from `web/service/use-*.ts` or create custom data hooks.
|
||||
|
||||
```typescript
|
||||
// ❌ Before: API logic in component
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import { AppInitializer } from '@/app/components/app-initializer'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import GA, { GaType } from '@/app/components/base/ga'
|
||||
import Zendesk from '@/app/components/base/zendesk'
|
||||
|
|
@ -7,7 +8,6 @@ import GotoAnything from '@/app/components/goto-anything'
|
|||
import Header from '@/app/components/header'
|
||||
import HeaderWrapper from '@/app/components/header/header-wrapper'
|
||||
import ReadmePanel from '@/app/components/plugins/readme-panel'
|
||||
import SwrInitializer from '@/app/components/swr-initializer'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import { EventEmitterContextProvider } from '@/context/event-emitter'
|
||||
import { ModalContextProvider } from '@/context/modal-context'
|
||||
|
|
@ -20,7 +20,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
<>
|
||||
<GA gaType={GaType.admin} />
|
||||
<AmplitudeProvider />
|
||||
<SwrInitializer>
|
||||
<AppInitializer>
|
||||
<AppContextProvider>
|
||||
<EventEmitterContextProvider>
|
||||
<ProviderContextProvider>
|
||||
|
|
@ -38,7 +38,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
</EventEmitterContextProvider>
|
||||
</AppContextProvider>
|
||||
<Zendesk />
|
||||
</SwrInitializer>
|
||||
</AppInitializer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import { AppInitializer } from '@/app/components/app-initializer'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import GA, { GaType } from '@/app/components/base/ga'
|
||||
import HeaderWrapper from '@/app/components/header/header-wrapper'
|
||||
import SwrInitor from '@/app/components/swr-initializer'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import { EventEmitterContextProvider } from '@/context/event-emitter'
|
||||
import { ModalContextProvider } from '@/context/modal-context'
|
||||
|
|
@ -15,7 +15,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
<>
|
||||
<GA gaType={GaType.admin} />
|
||||
<AmplitudeProvider />
|
||||
<SwrInitor>
|
||||
<AppInitializer>
|
||||
<AppContextProvider>
|
||||
<EventEmitterContextProvider>
|
||||
<ProviderContextProvider>
|
||||
|
|
@ -30,7 +30,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
</ProviderContextProvider>
|
||||
</EventEmitterContextProvider>
|
||||
</AppContextProvider>
|
||||
</SwrInitor>
|
||||
</AppInitializer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import type { ReactNode } from 'react'
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { SWRConfig } from 'swr'
|
||||
import {
|
||||
EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION,
|
||||
EDUCATION_VERIFYING_LOCALSTORAGE_ITEM,
|
||||
|
|
@ -11,12 +10,13 @@ import {
|
|||
import { fetchSetupStatus } from '@/service/common'
|
||||
import { resolvePostLoginRedirect } from '../signin/utils/post-login-redirect'
|
||||
|
||||
type SwrInitializerProps = {
|
||||
type AppInitializerProps = {
|
||||
children: ReactNode
|
||||
}
|
||||
const SwrInitializer = ({
|
||||
|
||||
export const AppInitializer = ({
|
||||
children,
|
||||
}: SwrInitializerProps) => {
|
||||
}: AppInitializerProps) => {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
// Tokens are now stored in cookies, no need to check localStorage
|
||||
|
|
@ -69,20 +69,5 @@ const SwrInitializer = ({
|
|||
})()
|
||||
}, [isSetupFinished, router, pathname, searchParams])
|
||||
|
||||
return init
|
||||
? (
|
||||
<SWRConfig value={{
|
||||
shouldRetryOnError: false,
|
||||
revalidateOnFocus: false,
|
||||
dedupingInterval: 60000,
|
||||
focusThrottleInterval: 5000,
|
||||
provider: () => new Map(),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SWRConfig>
|
||||
)
|
||||
: null
|
||||
return init ? children : null
|
||||
}
|
||||
|
||||
export default SwrInitializer
|
||||
|
|
@ -76,7 +76,7 @@ const config: KnipConfig = {
|
|||
// Browser initialization (runs on client startup)
|
||||
'app/components/browser-initializer.tsx!',
|
||||
'app/components/sentry-initializer.tsx!',
|
||||
'app/components/swr-initializer.tsx!',
|
||||
'app/components/app-initializer.tsx!',
|
||||
|
||||
// i18n initialization (server and client)
|
||||
'app/components/i18n.tsx!',
|
||||
|
|
|
|||
|
|
@ -138,7 +138,6 @@
|
|||
"semver": "^7.7.3",
|
||||
"sharp": "^0.33.5",
|
||||
"sortablejs": "^1.15.6",
|
||||
"swr": "^2.3.6",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tldts": "^7.0.17",
|
||||
"use-context-selector": "^2.0.0",
|
||||
|
|
|
|||
|
|
@ -330,9 +330,6 @@ importers:
|
|||
sortablejs:
|
||||
specifier: ^1.15.6
|
||||
version: 1.15.6
|
||||
swr:
|
||||
specifier: ^2.3.6
|
||||
version: 2.3.7(react@19.2.3)
|
||||
tailwind-merge:
|
||||
specifier: ^2.6.0
|
||||
version: 2.6.0
|
||||
|
|
@ -7950,11 +7947,6 @@ packages:
|
|||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
swr@2.3.7:
|
||||
resolution: {integrity: sha512-ZEquQ82QvalqTxhBVv/DlAg2mbmUjF4UgpPg9wwk4ufb9rQnZXh1iKyyKBqV6bQGu1Ie7L1QwSYO07qFIa1p+g==}
|
||||
peerDependencies:
|
||||
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
symbol-tree@3.2.4:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
|
||||
|
|
@ -17385,12 +17377,6 @@ snapshots:
|
|||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
swr@2.3.7(react@19.2.3):
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
react: 19.2.3
|
||||
use-sync-external-store: 1.6.0(react@19.2.3)
|
||||
|
||||
symbol-tree@3.2.4: {}
|
||||
|
||||
synckit@0.11.11:
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ Features Detected:
|
|||
${analysis.hasEvents ? '✓' : '✗'} Event handlers
|
||||
${analysis.hasRouter ? '✓' : '✗'} Next.js routing
|
||||
${analysis.hasAPI ? '✓' : '✗'} API calls
|
||||
${analysis.hasSWR ? '✓' : '✗'} SWR data fetching
|
||||
${analysis.hasReactQuery ? '✓' : '✗'} React Query
|
||||
${analysis.hasAhooks ? '✓' : '✗'} ahooks
|
||||
${analysis.hasForwardRef ? '✓' : '✗'} Ref forwarding (forwardRef)
|
||||
|
|
@ -236,7 +235,7 @@ Create the test file at: ${testPath}
|
|||
// ===== API Calls =====
|
||||
if (analysis.hasAPI) {
|
||||
guidelines.push('🌐 API calls detected:')
|
||||
guidelines.push(' - Mock API calls/hooks (useSWR, useQuery, fetch, etc.)')
|
||||
guidelines.push(' - Mock API calls/hooks (useQuery, useMutation, fetch, etc.)')
|
||||
guidelines.push(' - Test loading, success, and error states')
|
||||
guidelines.push(' - Focus on component behavior, not the data fetching lib')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export class ComponentAnalyzer {
|
|||
const resolvedPath = absolutePath ?? path.resolve(process.cwd(), filePath)
|
||||
const fileName = path.basename(filePath, path.extname(filePath))
|
||||
const lineCount = code.split('\n').length
|
||||
const hasReactQuery = /\buse(?:Query|Queries|InfiniteQuery|SuspenseQuery|SuspenseInfiniteQuery|Mutation)\b/.test(code)
|
||||
|
||||
// Calculate complexity metrics
|
||||
const { total: rawComplexity, max: rawMaxComplexity } = this.calculateCognitiveComplexity(code)
|
||||
|
|
@ -44,14 +45,13 @@ export class ComponentAnalyzer {
|
|||
hasMemo: code.includes('useMemo'),
|
||||
hasEvents: /on[A-Z]\w+/.test(code),
|
||||
hasRouter: code.includes('useRouter') || code.includes('usePathname'),
|
||||
hasAPI: code.includes('service/') || code.includes('fetch(') || code.includes('useSWR'),
|
||||
hasAPI: code.includes('service/') || code.includes('fetch(') || hasReactQuery,
|
||||
hasForwardRef: code.includes('forwardRef'),
|
||||
hasComponentMemo: /React\.memo|memo\(/.test(code),
|
||||
hasSuspense: code.includes('Suspense') || /\blazy\(/.test(code),
|
||||
hasPortal: code.includes('createPortal'),
|
||||
hasImperativeHandle: code.includes('useImperativeHandle'),
|
||||
hasSWR: code.includes('useSWR'),
|
||||
hasReactQuery: code.includes('useQuery') || code.includes('useMutation'),
|
||||
hasReactQuery,
|
||||
hasAhooks: code.includes('from \'ahooks\''),
|
||||
complexity,
|
||||
maxComplexity,
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ Usage: ${analysis.usageCount} reference${analysis.usageCount !== 1
|
|||
${analysis.hasRouter ? '✓' : '✗'} Next.js routing
|
||||
${analysis.hasAPI ? '✓' : '✗'} API calls
|
||||
${analysis.hasReactQuery ? '✓' : '✗'} React Query
|
||||
${analysis.hasSWR ? '✓' : '✗'} SWR (should migrate to React Query)
|
||||
${analysis.hasAhooks ? '✓' : '✗'} ahooks
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
|
|
@ -150,7 +149,7 @@ ${this.buildRequirements(analysis)}
|
|||
|
||||
Follow Dify project conventions:
|
||||
- Place extracted hooks in \`hooks/\` subdirectory or as \`use-<feature>.ts\`
|
||||
- Use React Query (\`@tanstack/react-query\`) for data fetching, not SWR
|
||||
- Use React Query (\`@tanstack/react-query\`) for data fetching
|
||||
- Follow existing patterns in \`web/service/use-*.ts\` for API hooks
|
||||
- Keep each new file under 300 lines
|
||||
- Maintain TypeScript strict typing
|
||||
|
|
@ -173,12 +172,8 @@ After refactoring, verify:
|
|||
}
|
||||
|
||||
// Priority 2: Extract API/data logic
|
||||
if (analysis.hasAPI && (analysis.hasEffects || analysis.hasSWR)) {
|
||||
if (analysis.hasSWR) {
|
||||
actions.push('🔄 MIGRATE SWR TO REACT QUERY: Replace useSWR with useQuery from @tanstack/react-query')
|
||||
}
|
||||
if (analysis.hasAPI)
|
||||
actions.push('🌐 EXTRACT DATA HOOK: Move API calls and data fetching logic into a dedicated hook using React Query')
|
||||
}
|
||||
|
||||
// Priority 3: Split large components
|
||||
if (analysis.lineCount > 300) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { Fetcher } from 'swr'
|
||||
import type { AnnotationCreateResponse, AnnotationEnableStatus, AnnotationItemBasic, EmbeddingModelConfig } from '@/app/components/app/annotation/type'
|
||||
import { ANNOTATION_DEFAULT } from '@/config'
|
||||
import { del, get, post } from './base'
|
||||
|
|
@ -44,11 +43,11 @@ export const addAnnotation = (appId: string, body: AnnotationItemBasic) => {
|
|||
return post<AnnotationCreateResponse>(`apps/${appId}/annotations`, { body })
|
||||
}
|
||||
|
||||
export const annotationBatchImport: Fetcher<{ job_id: string, job_status: string }, { url: string, body: FormData }> = ({ url, body }) => {
|
||||
export const annotationBatchImport = ({ url, body }: { url: string, body: FormData }): Promise<{ job_id: string, job_status: string }> => {
|
||||
return post<{ job_id: string, job_status: string }>(url, { body }, { bodyStringify: false, deleteContentType: true })
|
||||
}
|
||||
|
||||
export const checkAnnotationBatchImportProgress: Fetcher<{ job_id: string, job_status: string }, { jobID: string, appId: string }> = ({ jobID, appId }) => {
|
||||
export const checkAnnotationBatchImportProgress = ({ jobID, appId }: { jobID: string, appId: string }): Promise<{ job_id: string, job_status: string }> => {
|
||||
return get<{ job_id: string, job_status: string }>(`/apps/${appId}/annotations/batch-import-status/${jobID}`)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const fetchAppDetail = ({ url, id }: { url: string, id: string }): Promis
|
|||
return get<AppDetailResponse>(`${url}/${id}`)
|
||||
}
|
||||
|
||||
// Direct API call function for non-SWR usage
|
||||
// Direct API call function for one-off usage
|
||||
export const fetchAppDetailDirect = async ({ url, id }: { url: string, id: string }): Promise<AppDetailResponse> => {
|
||||
return get<AppDetailResponse>(`${url}/${id}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { Fetcher } from 'swr'
|
||||
import type {
|
||||
MarketplaceCollectionPluginsResponse,
|
||||
MarketplaceCollectionsResponse,
|
||||
|
|
@ -82,11 +81,11 @@ export const fetchPluginInfoFromMarketPlace = async ({
|
|||
return getMarketplace<{ data: { plugin: PluginInfoFromMarketPlace, version: { version: string } } }>(`/plugins/${org}/${name}`)
|
||||
}
|
||||
|
||||
export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string }> = ({ url }) => {
|
||||
export const fetchMarketplaceCollections = ({ url }: { url: string }): Promise<MarketplaceCollectionsResponse> => {
|
||||
return get<MarketplaceCollectionsResponse>(url)
|
||||
}
|
||||
|
||||
export const fetchMarketplaceCollectionPlugins: Fetcher<MarketplaceCollectionPluginsResponse, { url: string }> = ({ url }) => {
|
||||
export const fetchMarketplaceCollectionPlugins = ({ url }: { url: string }): Promise<MarketplaceCollectionPluginsResponse> => {
|
||||
return get<MarketplaceCollectionPluginsResponse>(url)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue