mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-03 11:37:36 +00:00
- 16 cross-shell utilities converted to sh/bash chezmoi executables: ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj (ghg-clone not named ghg: unrelated cargo binary owns that name) - cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files - fish keeps only shell-state functions; converted ones now hit PATH - cp-appid ignore gating moved to .local/bin path - dot_agents/skills: 82 skills materialized from dotstate storage so dot_claude skill symlinks resolve on fresh bootstrap; dotstate profile/symlinks.json released .agents/skills .bashrc .zshrc
4.8 KiB
4.8 KiB
persist-queries: Configure Query Persistence for Offline Support
Priority: LOW
Explanation
TanStack Query can persist the cache to storage (localStorage, IndexedDB, AsyncStorage) and restore it on app load. This enables offline support and faster startup by eliminating initial loading states.
Bad Example
// No persistence - always starts fresh
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyApp />
</QueryClientProvider>
)
}
// User refreshes page:
// 1. Empty cache
// 2. Loading spinners everywhere
// 3. Refetch all data
// Poor offline experience
Good Example: Basic Persistence with localStorage
import { QueryClient } from '@tanstack/react-query'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24, // 24 hours - keep cache longer for persistence
staleTime: 1000 * 60 * 5, // 5 minutes
},
},
})
const persister = createSyncStoragePersister({
storage: window.localStorage,
key: 'REACT_QUERY_CACHE',
})
function App() {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{
persister,
maxAge: 1000 * 60 * 60 * 24, // 24 hours max
}}
>
<MyApp />
</PersistQueryClientProvider>
)
}
Good Example: Async Persistence with IndexedDB
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'
import { get, set, del } from 'idb-keyval'
const persister = createAsyncStoragePersister({
storage: {
getItem: async (key) => await get(key),
setItem: async (key, value) => await set(key, value),
removeItem: async (key) => await del(key),
},
key: 'REACT_QUERY_CACHE',
})
function App() {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{
persister,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
buster: APP_VERSION, // Bust cache on app updates
}}
>
<MyApp />
</PersistQueryClientProvider>
)
}
Good Example: Selective Persistence
import { persistQueryClient } from '@tanstack/react-query-persist-client'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24,
},
},
})
// Only persist certain queries
persistQueryClient({
queryClient,
persister,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
// Don't persist user-specific sensitive data
if (query.queryKey[0] === 'user-session') return false
// Don't persist real-time data
if (query.queryKey[0] === 'notifications') return false
// Don't persist failed queries
if (query.state.status !== 'success') return false
// Persist everything else
return true
},
},
})
Good Example: React Native with AsyncStorage
import AsyncStorage from '@react-native-async-storage/async-storage'
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'
const persister = createAsyncStoragePersister({
storage: AsyncStorage,
key: 'app-query-cache',
})
// Usage is the same as web
Good Example: Handling Restoration Loading
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
function App() {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
onSuccess={() => {
// Cache restored successfully
console.log('Cache restored')
}}
>
{/* Show loading while restoring */}
<PersistQueryClientProvider.Consumer>
{({ isRestoring }) =>
isRestoring ? <SplashScreen /> : <MainApp />
}
</PersistQueryClientProvider.Consumer>
</PersistQueryClientProvider>
)
}
// Or use the hook
function MainApp() {
const { isRestoring } = usePersistQueryClientRestore()
if (isRestoring) return <SplashScreen />
return <App />
}
Persistence Configuration
| Option | Purpose |
|---|---|
maxAge |
Maximum cache age before considered invalid |
buster |
String to invalidate cache (use app version) |
dehydrateOptions.shouldDehydrateQuery |
Filter which queries to persist |
hydrateOptions.shouldHydrate |
Filter which queries to restore |
Context
- Requires
@tanstack/react-query-persist-clientpackage - Set
gcTimehigher than default (5 min) for persistence to be useful - Use
busteroption to invalidate cache on app updates - Don't persist sensitive data or real-time data
- IndexedDB is better than localStorage for large caches
- Restored data is still subject to staleTime checks
- Works well with
networkMode: 'offlineFirst'