Fix workflow knowledge retrieval cache bug

🐛 Fixed issue where deleted datasets appeared as blank options in workflow knowledge retrieval nodes

## Problem
- After deleting a dataset and adding a new one, workflow knowledge retrieval nodes showed both a blank option (ghost data) and the correct new dataset
- Issue was caused by stale SWR cache and improper initialization of SelectDataset component

## Root Causes
1. SelectDataset component created 'ghost' objects with only IDs but no names during initialization
2. SWR cache was not properly invalidated when datasets were deleted

## Solutions
1. **Fixed SelectDataset initialization**: Avoid creating ghost objects, properly initialize selected state after data loads
2. **Added SWR cache invalidation**: Clear all dataset-related cache when a dataset is deleted

## Files Changed
- web/app/components/app/configuration/dataset-config/select-dataset/index.tsx
- web/app/(commonLayout)/datasets/DatasetCard.tsx
- clickzetta/build-local-dify-web.sh (new build script for testing)

## Testing
 Verified in Docker environment: ghost options no longer appear after dataset deletion
 Page refresh is no longer needed to see correct state

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
pull/22673/head
yunqiqiliang 10 months ago
parent 18230d12f9
commit 5a7d32e2ab

@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiMoreFill } from '@remixicon/react' import { RiMoreFill } from '@remixicon/react'
import { mutate } from 'swr'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import Confirm from '@/app/components/base/confirm' import Confirm from '@/app/components/base/confirm'
import { ToastContext } from '@/app/components/base/toast' import { ToastContext } from '@/app/components/base/toast'
@ -57,6 +58,14 @@ const DatasetCard = ({
const onConfirmDelete = useCallback(async () => { const onConfirmDelete = useCallback(async () => {
try { try {
await deleteDataset(dataset.id) await deleteDataset(dataset.id)
// Clear SWR cache to prevent stale data in knowledge retrieval nodes
mutate(
key => typeof key === 'string' && key.includes('/datasets'),
undefined,
{ revalidate: true }
)
notify({ type: 'success', message: t('dataset.datasetDeleted') }) notify({ type: 'success', message: t('dataset.datasetDeleted') })
if (onSuccess) if (onSuccess)
onSuccess() onSuccess()

@ -30,7 +30,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
onSelect, onSelect,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [selected, setSelected] = React.useState<DataSet[]>(selectedIds.map(id => ({ id }) as any)) const [selected, setSelected] = React.useState<DataSet[]>([])
const [loaded, setLoaded] = React.useState(false) const [loaded, setLoaded] = React.useState(false)
const [datasets, setDataSets] = React.useState<DataSet[] | null>(null) const [datasets, setDataSets] = React.useState<DataSet[] | null>(null)
const hasNoData = !datasets || datasets?.length === 0 const hasNoData = !datasets || datasets?.length === 0
@ -50,19 +50,14 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
const newList = [...(datasets || []), ...data.filter(item => item.indexing_technique || item.provider === 'external')] const newList = [...(datasets || []), ...data.filter(item => item.indexing_technique || item.provider === 'external')]
setDataSets(newList) setDataSets(newList)
setLoaded(true) setLoaded(true)
if (!selected.find(item => !item.name))
return { list: [] }
const newSelected = produce(selected, (draft) => { // Initialize selected datasets based on selectedIds and available datasets
selected.forEach((item, index) => { if (selected.length === 0 && selectedIds.length > 0) {
if (!item.name) { // not fetched database const validSelectedDatasets = selectedIds
const newItem = newList.find(i => i.id === item.id) .map(id => newList.find(item => item.id === id))
if (newItem) .filter(Boolean) as DataSet[]
draft[index] = newItem setSelected(validSelectedDatasets)
} }
})
})
setSelected(newSelected)
} }
return { list: [] } return { list: [] }
}, },

Loading…
Cancel
Save