initial commit
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
# network-mode: Configure Network Mode for Offline Support
|
||||
|
||||
## Priority: LOW
|
||||
|
||||
## Explanation
|
||||
|
||||
TanStack Query's `networkMode` controls how queries and mutations behave when there's no network connection. Configure it based on your app's offline requirements: always fetch, pause when offline, or work entirely offline.
|
||||
|
||||
## Bad Example
|
||||
|
||||
```tsx
|
||||
// Not considering offline behavior
|
||||
const { data } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: fetchTodos,
|
||||
// Default networkMode: 'online'
|
||||
// Query pauses with no feedback when offline
|
||||
})
|
||||
|
||||
// User goes offline, sees stale data with no indication
|
||||
// Mutations silently queue with no UI feedback
|
||||
```
|
||||
|
||||
## Good Example: Default Online Mode with Offline UI
|
||||
|
||||
```tsx
|
||||
// Show clear offline state to users
|
||||
function TodoList() {
|
||||
const { data, fetchStatus, status } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: fetchTodos,
|
||||
networkMode: 'online', // Default - pauses when offline
|
||||
})
|
||||
|
||||
// fetchStatus: 'fetching' | 'paused' | 'idle'
|
||||
// 'paused' means waiting for network
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fetchStatus === 'paused' && (
|
||||
<Banner>You're offline. Showing cached data.</Banner>
|
||||
)}
|
||||
<TodoItems todos={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Good Example: Always Mode for Offline-First
|
||||
|
||||
```tsx
|
||||
// App works offline with local data
|
||||
const { data, error } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: async () => {
|
||||
// Try network first
|
||||
try {
|
||||
const todos = await fetchTodosFromServer()
|
||||
await saveToLocalDB(todos) // Sync to local
|
||||
return todos
|
||||
} catch (e) {
|
||||
// Fall back to local data
|
||||
return getFromLocalDB()
|
||||
}
|
||||
},
|
||||
networkMode: 'always', // Always runs queryFn, even offline
|
||||
})
|
||||
|
||||
// Or set globally
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
networkMode: 'always',
|
||||
},
|
||||
mutations: {
|
||||
networkMode: 'always',
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Good Example: Offline-First Mode
|
||||
|
||||
```tsx
|
||||
// Only fetch when online, but don't fail when offline
|
||||
const { data } = useQuery({
|
||||
queryKey: ['user-preferences'],
|
||||
queryFn: fetchPreferences,
|
||||
networkMode: 'offlineFirst',
|
||||
// Runs queryFn once, then waits for network if it fails
|
||||
// Good for: data that's useful to attempt offline
|
||||
})
|
||||
```
|
||||
|
||||
## Good Example: Mutation Offline Queue
|
||||
|
||||
```tsx
|
||||
function TodoApp() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const addTodo = useMutation({
|
||||
mutationFn: createTodo,
|
||||
networkMode: 'online', // Pauses when offline
|
||||
onMutate: async (newTodo) => {
|
||||
// Optimistic update works offline
|
||||
await queryClient.cancelQueries({ queryKey: ['todos'] })
|
||||
const previous = queryClient.getQueryData(['todos'])
|
||||
queryClient.setQueryData(['todos'], (old: Todo[]) => [...old, newTodo])
|
||||
return { previous }
|
||||
},
|
||||
onError: (err, newTodo, context) => {
|
||||
queryClient.setQueryData(['todos'], context?.previous)
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
||||
},
|
||||
})
|
||||
|
||||
// Track paused mutations
|
||||
const pendingMutations = useMutationState({
|
||||
filters: { status: 'pending' },
|
||||
})
|
||||
|
||||
const pausedMutations = pendingMutations.filter(
|
||||
m => m.state.isPaused
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{pausedMutations.length > 0 && (
|
||||
<Banner>
|
||||
{pausedMutations.length} changes waiting to sync
|
||||
</Banner>
|
||||
)}
|
||||
<TodoList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Network Mode Comparison
|
||||
|
||||
| Mode | Behavior | Use Case |
|
||||
|------|----------|----------|
|
||||
| `'online'` (default) | Pauses when offline, resumes when online | Most apps, show offline state |
|
||||
| `'always'` | Always runs queryFn regardless of network | Offline-first apps, local-only data |
|
||||
| `'offlineFirst'` | Tries once, then waits for network if fails | Best-effort offline |
|
||||
|
||||
## Good Example: Online Status Detection
|
||||
|
||||
```tsx
|
||||
import { onlineManager } from '@tanstack/react-query'
|
||||
|
||||
// React to online/offline changes
|
||||
function NetworkStatus() {
|
||||
const isOnline = useSyncExternalStore(
|
||||
onlineManager.subscribe,
|
||||
() => onlineManager.isOnline(),
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={isOnline ? 'online' : 'offline'}>
|
||||
{isOnline ? 'Connected' : 'Offline'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Manually override online detection (for testing)
|
||||
onlineManager.setOnline(false)
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
- Default `'online'` mode is best for most apps
|
||||
- `fetchStatus: 'paused'` indicates waiting for network
|
||||
- Mutations queue automatically and retry when back online
|
||||
- Use `onlineManager` to detect and control online state
|
||||
- Combine with optimistic updates for seamless offline UX
|
||||
- Consider service workers for true offline support
|
||||
Reference in New Issue
Block a user