Optimistic update flow đầy đủ: khi user thực hiện action, update UI ngay lập tức trước khi API confirm, rollback nếu API fail.
- Implementation:
onMutate: async (newTodo) => { await queryClient.cancelQueries({ queryKey: ['todos'] }); const snapshot = queryClient.getQueryData(['todos']); queryClient.setQueryData(['todos'], (old) => [...old, { ...newTodo, id: 'temp-' + Date.now() }]); return { snapshot } }.onError: (err, newTodo, context) => { queryClient.setQueryData(['todos'], context.snapshot) }.onSettled: () => { queryClient.invalidateQueries({ queryKey: ['todos'] }) }. - Quan trọng:
cancelQueriestrước khi optimistic update — tránh race condition khi background refetch overwrite optimistic state. - Context pattern: onMutate return value trở thành
contexttrong onError/onSettled. - Khi nào dùng optimistic vs simple invalidation: optimistic cho actions user expect instant feedback (like, follow, toggle), simple invalidation cho actions phức tạp hơn (payment, create order) vì rollback UX tệ hơn là loading nhỏ.
- Lưu ý: optimistic ID ('temp-123') cần được replace bằng real ID sau khi server confirm — onSettled invalidate để refetch với real data.
Full optimistic update flow: when the user performs an action, update the UI immediately before the API confirms, then rollback if the API fails.
- Implementation:
onMutate: async (newTodo) => { await queryClient.cancelQueries({ queryKey: ['todos'] }); const snapshot = queryClient.getQueryData(['todos']); queryClient.setQueryData(['todos'], (old) => [...old, { ...newTodo, id: 'temp-' + Date.now() }]); return { snapshot } }.onError: (err, newTodo, context) => { queryClient.setQueryData(['todos'], context.snapshot) }.onSettled: () => { queryClient.invalidateQueries({ queryKey: ['todos'] }) }. - Important:
cancelQueriesbefore the optimistic update — prevents race conditions where a background refetch overwrites the optimistic state. - Context pattern: the onMutate return value becomes
contextin onError/onSettled. - When to use optimistic vs simple invalidation: optimistic for actions where users expect instant feedback (like, follow, toggle); simple invalidation for complex actions (payment, creating an order) because rollback UX is worse than a small loading indicator.
Pitfall: optimistic IDs ('temp-123') must be replaced with real IDs after server confirmation — onSettled invalidates to refetch with real data.