Problem
After uploading new trade files through an "Upload New File" dialog, the trade reconciliation table shows stale data from the previous upload instead of the updated data. The table only refreshes when I manually invalidate the query ["reconciliation","trade",127] using React Query DevTools. I want the table to automatically show updated data immediately after the upload dialog closes.
Current Behavior
- User clicks "Upload New File" button in Trade Reconciliation page
- User uploads new trade files via dialog
- Dialog closes and shows success toast
- Trade reconciliation table still shows old data
- Manual cache invalidation via DevTools → table updates correctly
Expected Behavior
Trade reconciliation table should automatically display updated data after file upload without manual intervention.
Code Structure
Main Page (ReconciliationPage.tsx)
The main page handles file uploads and manages the overall reconciliation process:
const handleFileUploadSubmit = async (filesData: UploadedFilesData) => {
try {
const payload = Object.entries(filesData).flatMap(([fileType, uploadedFiles]) =>
uploadedFiles.map((file: UploadedFile) => ({
fileName: file.name,
fileType,
fileUrl: file.url,
fileUploadBy: 'currentUser',
processTrackingId: processTrackingId,
}))
);
const response = await sendUploadedFilesData(payload);
await Promise.all([
queryClient.invalidateQueries({ queryKey: ['reconciliation', 'uploadedFiles'] }),
queryClient.invalidateQueries({ queryKey: ['reconciliation', 'processTracking'] }),
queryClient.invalidateQueries({ queryKey: ['reconciliation', 'trade', processTrackingId] }),
]);
const updatedProcessTracking = await processTrackingQuery.refetch();
if (updatedProcessTracking.data) {
const { processTrackingId: newProcessTrackingId, hasExtractedData: newHasExtractedData } = updatedProcessTracking.data;
setProcessTrackingId(newProcessTrackingId);
setHasExtractedData(newHasExtractedData);
}
toast.success('Files uploaded successfully!');
return response;
} catch (error) {
handleApiError(error, 'Failed to upload files');
throw error;
}
};
Trade Reconciliation Component
const handleUploadConfirm = async (filesData: UploadedFilesData) => {
try {
setLoading(true);
await onFileUploadSubmit(filesData);
const extractResult = await onExtractData();
if (processTrackingId) {
await Promise.all([
queryClient.invalidateQueries({
queryKey: ['reconciliation', 'extractedData', processTrackingId],
}),
queryClient.invalidateQueries({
queryKey: ['reconciliation', 'trade', processTrackingId],
}),
queryClient.invalidateQueries({
queryKey: ['reconciliation', 'processTracking'],
}),
queryClient.invalidateQueries({
queryKey: ['reconciliation', 'uploadedFiles'],
}),
]);
await queryClient.refetchQueries({
queryKey: ['reconciliation', 'trade', processTrackingId],
});
}
setOpenFileUploadDialog(false);
toast.success('Files uploaded successfully! Trade data has been updated.');
} catch (error) {
console.error('❌ Upload failed:', error);
toast.error('Failed to upload files');
} finally {
setLoading(false);
}
};
React Query Hook
const tradeQuery = useQuery({
queryKey: [QUERY_KEYS.TRADE, processTrackingId],
queryFn: () => getTradeReconciliationData(processTrackingId),
enabled: currentStep >= 1 && !!processTrackingId,
staleTime: CACHE_CONFIG.SHORT, // 5 minutes
});
What I've Tried
- Multiple
invalidateQueriescalls with exact query keys refetchQueriesafter invalidation- Awaiting all invalidation promises
- Different combinations of query key structures
Question
Why does manual DevTools invalidation work but programmatic invalidation doesn't?
Environment
- React Query (TanStack Query) v5
- Next.js
- TypeScript
Any insights into why the automatic cache invalidation isn't working would be greatly appreciated!