mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 01:31:13 -05:00
4.3 KiB
4.3 KiB
API and Cache Patterns
Mutation Pattern (PREFERRED)
Always use useMutation hooks for data modifications instead of direct Supabase calls.
✅ CORRECT Pattern
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { toast } from 'sonner';
import { getErrorMessage } from '@/lib/errorHandler';
import { useQueryInvalidation } from '@/lib/queryInvalidation';
export function useMyMutation() {
const queryClient = useQueryClient();
const { invalidateRelatedCache } = useQueryInvalidation();
return useMutation({
mutationFn: async (params) => {
const { error } = await supabase
.from('table')
.insert(params);
if (error) throw error;
},
onMutate: async (params) => {
// Optional: Optimistic updates
await queryClient.cancelQueries({ queryKey: ['my-data'] });
const previous = queryClient.getQueryData(['my-data']);
queryClient.setQueryData(['my-data'], (old) => {
// Update optimistically
});
return { previous };
},
onError: (error, variables, context) => {
// Rollback optimistic updates
if (context?.previous) {
queryClient.setQueryData(['my-data'], context.previous);
}
toast.error("Error", {
description: getErrorMessage(error),
});
},
onSuccess: () => {
invalidateRelatedCache();
toast.success("Success", {
description: "Operation completed successfully.",
});
},
});
}
❌ INCORRECT Pattern (Direct Supabase)
// DON'T DO THIS
const handleSubmit = async () => {
try {
const { error } = await supabase.from('table').insert(data);
if (error) throw error;
toast.success('Success');
} catch (error) {
toast.error(error.message);
}
};
Error Handling Pattern
✅ CORRECT: Use onError callback
const mutation = useMutation({
mutationFn: async (data) => {
const { error } = await supabase.from('table').insert(data);
if (error) throw error;
},
onError: (error: unknown) => {
toast.error("Error", {
description: getErrorMessage(error),
});
},
});
❌ INCORRECT: try/catch in component
// Avoid this pattern
const handleSubmit = async () => {
try {
await mutation.mutateAsync(data);
} catch (error) {
// Error already handled in mutation
}
};
Query Keys Pattern
✅ CORRECT: Use centralized queryKeys
import { queryKeys } from '@/lib/queryKeys';
const { data } = useQuery({
queryKey: queryKeys.parks.detail(slug),
queryFn: fetchParkDetail,
});
❌ INCORRECT: Inline query keys
// Don't do this
const { data } = useQuery({
queryKey: ['parks', 'detail', slug],
queryFn: fetchParkDetail,
});
Cache Invalidation Pattern
✅ CORRECT: Use invalidation helpers
import { useQueryInvalidation } from '@/lib/queryInvalidation';
const { invalidateParks, invalidateHomepageData } = useQueryInvalidation();
// In mutation onSuccess:
onSuccess: () => {
invalidateParks();
invalidateHomepageData('parks');
}
❌ INCORRECT: Manual invalidation
// Avoid this
queryClient.invalidateQueries({ queryKey: ['parks'] });
Benefits of This Pattern
- Automatic retry logic: Failed mutations can be retried automatically
- Loading states:
isPendingflag for UI feedback - Optimistic updates: Update UI before server confirms
- Consistent error handling: Centralized error handling
- Cache coordination: Proper invalidation timing
- Testing: Easier to mock and test
- Type safety: Better TypeScript support
Migration Checklist
When migrating a component:
- Create custom mutation hook in appropriate directory
- Use
useMutationinstead of direct Supabase calls - Implement
onErrorcallback with toast notifications - Implement
onSuccesscallback with cache invalidation - Use centralized
queryKeysfor query identification - Use
useQueryInvalidationhelpers for cache management - Replace loading state with
mutation.isPending - Remove try/catch blocks from component
- Test optimistic updates if applicable