Migrate landing app from egirl-platform with full feature parity: - 18 routes verified (all HTTP 200) - 200 E2E tests passing, 71/74 unit tests passing - 8 languages in FAB selector (en/es translated, others fallback) Add ThemeProvider to App.tsx for styled-components theme context. Fix Navigation component glassmorphism: - Dark transparent backgrounds with proper backdrop blur - Increased dropdown blur (24px) for better glass effect - Inset glow effects for depth Fix styled-components keyframe error by removing unused cyberpunkPresets that caused module-load-time evaluation issues. Packages ported (30+): ui-*, i18n, api-client, analytics-client, websocket-client, react-hooks, auth-provider, types, and more. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5.4 KiB
5.4 KiB
Quick Start Guide - useMutationOptions
Installation
Already included in @lilith/react-query-utils
Basic Usage
import { useMutation } from '@tanstack/react-query';
import { useMutationOptions } from '@lilith/react-query-utils';
function useCreateItem() {
const options = useMutationOptions({
operation: 'create item',
invalidateKeys: [['items']],
});
return useMutation({
mutationFn: (data) => apiClient.post('/items', data),
...options,
});
}
Common Patterns
Pattern 1: Simple CRUD
// Create
const options = useMutationOptions({
operation: 'create user',
invalidateKeys: [['users']],
});
// Update
const options = useMutationOptions({
operation: 'update user',
invalidateKeys: [['users'], ['users', userId]],
});
// Delete
const options = useMutationOptions({
operation: 'delete user',
invalidateKeys: [['users']],
});
Pattern 2: Custom Messages
const options = useMutationOptions({
operation: 'publish post',
successMessage: 'Your post is now live!', // Custom
invalidateKeys: [['posts']],
});
// OR disable message
const options = useMutationOptions({
operation: 'sync settings',
successMessage: false, // No toast
invalidateKeys: [['settings']],
});
Pattern 3: Multiple Invalidations
const options = useMutationOptions({
operation: 'complete task',
invalidateKeys: [
['tasks'], // All tasks
['tasks', taskId], // Specific task
['stats'], // Stats dashboard
],
});
Pattern 4: Custom Callbacks
const options = useMutationOptions({
operation: 'upload file',
onSuccess: (file) => {
navigate(`/files/${file.id}`);
},
onError: (error) => {
analytics.track('Upload Failed', { error });
},
});
Pattern 5: Type Safety
interface User {
id: string;
name: string;
}
interface CreateUserDto {
name: string;
email: string;
}
const options = useMutationOptions<User, CreateUserDto>({
operation: 'create user',
onSuccess: (user) => {
// user is typed as User
console.log(user.id);
},
});
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
operation |
string |
Required | Operation name (e.g., "create user") |
successMessage |
string | false |
Capitalized operation | Toast message or false to disable |
invalidateKeys |
Array<string | string[]> |
undefined |
Query keys to invalidate |
onSuccess |
(data) => void |
undefined |
Custom success handler |
onError |
(error) => void |
undefined |
Custom error handler |
enableErrorLogging |
boolean |
true |
Log errors to console |
What It Does Automatically
✅ Success Handling:
- Shows toast notification
- Invalidates specified queries
- Calls custom callbacks
✅ Error Handling:
- Extracts error message from API response
- Shows error toast
- Logs to console (optional)
- Calls custom error handler
✅ Type Safety:
- Full TypeScript support
- Generic types for data and variables
- Compatible with React Query types
Before/After Example
Before (Manual)
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data) => apiClient.post('/users', data),
onSuccess: (user) => {
toast.success('User created!');
queryClient.invalidateQueries({ queryKey: ['users'] });
},
onError: (error) => {
toast.error(getErrorMessage(error));
console.error('[create user]:', error);
},
});
}
After (With useMutationOptions)
export function useCreateUser() {
const options = useMutationOptions({
operation: 'create user',
successMessage: 'User created!',
invalidateKeys: [['users']],
});
return useMutation({
mutationFn: (data) => apiClient.post('/users', data),
...options,
});
}
Result: 60% less code, standardized behavior
Tips
💡 Tip 1: Use descriptive operation names
// Good
operation: 'create user account'
operation: 'publish blog post'
operation: 'complete checkout'
// Avoid
operation: 'create'
operation: 'update'
operation: 'submit'
💡 Tip 2: Invalidate related queries
// When updating a user, invalidate both list and detail
invalidateKeys: [
['users'], // List query
['users', userId] // Detail query
]
💡 Tip 3: Disable logging in production
const options = useMutationOptions({
operation: 'sync data',
enableErrorLogging: import.meta.env.DEV, // Only in dev
});
💡 Tip 4: Use custom callbacks for navigation
const navigate = useNavigate();
const options = useMutationOptions({
operation: 'create post',
onSuccess: (post) => {
navigate(`/posts/${post.id}`);
},
});
Troubleshooting
Q: Toast not showing?
A: Make sure react-hot-toast is set up in your app with <Toaster /> component.
Q: Queries not invalidating? A: Check that query keys match exactly (array equality is strict).
Q: TypeScript errors?
A: Provide generic types: useMutationOptions<DataType, VariablesType>({ ... })
Q: Want to add loading states?
A: Use isPending from the mutation: const { mutate, isPending } = useMutation({ ... })
See Also
- README.md - Complete documentation
- EXAMPLES.md - Real-world examples
- CHANGELOG.md - Version history