# Optimistic Updates Guide ## FE-API-018: Optimistic UI Updates for Better UX This guide explains how to implement optimistic updates in Veza frontend components to provide instant feedback to users. ## What are Optimistic Updates? Optimistic updates allow the UI to update immediately before the server responds, making the app feel faster and more responsive. If the request fails, the UI is rolled back to the previous state. ## Utilities Available ### 1. `createOptimisticUpdate` For general optimistic updates: ```typescript import { useQueryClient } from '@tanstack/react-query'; import { createOptimisticUpdate } from '@/utils/optimisticUpdates'; const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: updatePlaylist, ...createOptimisticUpdate({ queryClient, queryKeys: [['playlist', playlistId], ['playlists']], optimisticData: (variables) => ({ ...currentPlaylist, ...variables, }), }), }); ``` ### 2. `createArrayOptimisticUpdate` For array operations (add, remove, update): ```typescript import { useQueryClient } from '@tanstack/react-query'; import { createArrayOptimisticUpdate } from '@/utils/optimisticUpdates'; const queryClient = useQueryClient(); // Add item const addMutation = useMutation({ mutationFn: addTrackToPlaylist, ...createArrayOptimisticUpdate({ queryClient, queryKeys: [['playlist', playlistId]], arrayQueryKey: ['playlist', playlistId, 'tracks'], operation: 'add', optimisticItem: (variables) => ({ id: `temp-${Date.now()}`, track_id: variables.trackId, ...variables, }), }), }); // Remove item const removeMutation = useMutation({ mutationFn: removeTrackFromPlaylist, ...createArrayOptimisticUpdate({ queryKeys: [['playlist', playlistId]], arrayQueryKey: ['playlist', playlistId, 'tracks'], operation: 'remove', matchItem: (item, variables) => item.id === variables.trackId, }), }); ``` ### 3. `createToggleOptimisticUpdate` For toggle operations (like/unlike, follow/unfollow): ```typescript import { useQueryClient } from '@tanstack/react-query'; import { createToggleOptimisticUpdate } from '@/utils/optimisticUpdates'; const queryClient = useQueryClient(); const likeMutation = useMutation({ mutationFn: () => likeTrack(trackId), ...createToggleOptimisticUpdate({ queryClient, queryKeys: [['track', trackId], ['tracks']], currentValue: isLiked, updateCount: true, countDelta: 1, }), onSuccess: () => { showSuccess('Ajouté aux favoris'); }, onError: (error) => { showError(error.message || "Erreur lors de l'ajout aux favoris"); }, }); ``` ## Best Practices 1. **Always provide rollback**: The utilities automatically rollback on error, but you can provide custom rollback logic if needed. 2. **Invalidate queries on success**: The utilities automatically invalidate queries on settle, but you can also invalidate on success for immediate refresh. 3. **Show user feedback**: Always show success/error messages to inform users of the result. 4. **Handle edge cases**: Consider what happens if the optimistic data structure doesn't match the actual response. 5. **Use for fast operations**: Optimistic updates work best for operations that typically complete quickly (< 500ms). ## Examples ### Example 1: Updating a Playlist ```typescript const queryClient = useQueryClient(); const updateMutation = useMutation({ mutationFn: (data: UpdatePlaylistRequest) => updatePlaylist(playlistId, data), ...createOptimisticUpdate({ queryClient, queryKeys: [['playlist', playlistId]], optimisticData: (variables) => ({ ...playlist, ...variables, }), }), onSuccess: () => { showSuccess('Playlist mise à jour'); }, }); ``` ### Example 2: Adding a Comment ```typescript const queryClient = useQueryClient(); const addCommentMutation = useMutation({ mutationFn: (content: string) => createComment(trackId, content), ...createArrayOptimisticUpdate({ queryClient, queryKeys: [['trackComments', trackId]], arrayQueryKey: ['trackComments', trackId], operation: 'add', optimisticItem: (variables) => ({ id: `temp-${Date.now()}`, content: variables, user: currentUser, created_at: new Date().toISOString(), }), }), onSuccess: () => { showSuccess('Commentaire ajouté'); }, }); ``` ### Example 3: Following a User ```typescript const queryClient = useQueryClient(); const followMutation = useMutation({ mutationFn: () => followUser(userId), ...createToggleOptimisticUpdate({ queryClient, queryKeys: [['user', userId], ['users']], currentValue: isFollowing, updateCount: true, countDelta: 1, }), onSuccess: () => { showSuccess('Vous suivez maintenant cet utilisateur'); }, }); ``` ## When NOT to Use Optimistic Updates - **Critical operations**: Don't use for operations that must be confirmed (e.g., payments, deletions) - **Slow operations**: Don't use for operations that take > 2 seconds - **Complex state**: Don't use if the optimistic update is too complex or error-prone - **Server-side validation**: Don't use if the server response might differ significantly from the optimistic data