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 { useTranslation } from 'react-i18next'
import { RiMoreFill } from '@remixicon/react'
import { mutate } from 'swr'
import cn from '@/utils/classnames'
import Confirm from '@/app/components/base/confirm'
import { ToastContext } from '@/app/components/base/toast'
@ -57,6 +58,14 @@ const DatasetCard = ({
const onConfirmDelete = useCallback(async () => {
try {
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') })
if (onSuccess)
onSuccess()

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

Loading…
Cancel
Save