Keeping data synchronized in real-time.
Polling
Simplest approach - refetch on interval:
jsxuseQuery({ queryKey: ['notifications'], queryFn: fetchNotifications, refetchInterval: 30000, // Every 30 seconds refetchIntervalInBackground: false, // Pause when tab hidden });
Conditional Polling
jsxconst { data } = useQuery({ queryKey: ['order', orderId], queryFn: () => fetchOrder(orderId), refetchInterval: (query) => { // Poll while order is processing return query.state.data?.status === 'processing' ? 5000 : false; }, });
WebSocket Integration
jsximport { useEffect } from 'react'; import { useQueryClient } from '@tanstack/react-query'; function useWebSocketUpdates() { const queryClient = useQueryClient(); useEffect(() => { const ws = new WebSocket('wss://api.example.com/ws'); ws.onmessage = (event) => { const message = JSON.parse(event.data); switch (message.type) { case 'TODO_CREATED': queryClient.invalidateQueries({ queryKey: ['todos'] }); break; case 'TODO_UPDATED': queryClient.setQueryData( ['todo', message.data.id], message.data ); break; case 'TODO_DELETED': queryClient.setQueryData(['todos'], (old) => old?.filter(t => t.id !== message.data.id) ); break; } }; return () => ws.close(); }, [queryClient]); }
Server-Sent Events (SSE)
jsxfunction useSSEUpdates(url) { const queryClient = useQueryClient(); useEffect(() => { const eventSource = new EventSource(url); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); queryClient.setQueryData(['realtime-data'], data); }; eventSource.onerror = () => { eventSource.close(); }; return () => eventSource.close(); }, [url, queryClient]); }
Optimistic + Real-Time
jsxfunction useTodoMutation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: createTodo, onMutate: async (newTodo) => { // Optimistic update await queryClient.cancelQueries({ queryKey: ['todos'] }); const previous = queryClient.getQueryData(['todos']); queryClient.setQueryData(['todos'], (old) => [ ...old, { ...newTodo, id: 'temp-' + Date.now(), pending: true } ]); return { previous }; }, // Real-time WebSocket will provide the actual update // If it doesn't arrive, onSettled refetches onSettled: () => { queryClient.invalidateQueries({ queryKey: ['todos'] }); }, }); }