@uecsio/query-cache
Version:
Cache management utilities for TanStack Query (Vue Query)
424 lines (313 loc) âĒ 9.02 kB
Markdown
A lightweight utility library for managing [TanStack Query](https://tanstack.com/query) (Vue Query) cache in Vue 3 applications.
- ðŊ **Simple API** - Intuitive functions for common cache operations
- ð§ **Type-safe** - Full TypeScript support
- ðŠķ **Lightweight** - Minimal bundle size, only wraps TanStack Query
- ð **Zero config** - Works out of the box with Vue Query
- ðĶ **Tree-shakeable** - Import only what you need
```bash
npm install @uecsio/query-cache
```
**Peer Dependencies:**
```bash
npm install vue @tanstack/vue-query
```
Make sure you have TanStack Query configured in your Vue app:
```javascript
// main.js
import { createApp } from 'vue'
import { VueQueryPlugin } from '@tanstack/vue-query'
import App from './App.vue'
const app = createApp(App)
app.use(VueQueryPlugin)
app.mount('#app')
```
```javascript
import {
clearCache,
invalidateCache,
clearAllCache
} from '@uecsio/query-cache'
// Clear specific query cache
clearCache(['users', 1])
// Clear all queries matching pattern
clearCache(['users']) // Clears all queries starting with 'users'
// Invalidate cache (mark as stale and refetch)
invalidateCache(['users', 1])
// Clear all application cache
clearAllCache()
```
```vue
<script setup>
import { invalidateCache } from '@uecsio/query-cache'
async function createUser(userData) {
await api.post('/users', userData)
// Invalidate users list to show new user
invalidateCache(['users'])
}
async function updateUser(id, userData) {
await api.put(`/users/${id}`, userData)
// Invalidate specific user and list
invalidateCache(['users', id])
invalidateCache(['users'])
}
async function deleteUser(id) {
await api.delete(`/users/${id}`)
// Clear deleted user from cache
clearCache(['users', id])
invalidateCache(['users'])
}
</script>
```
```javascript
import {
getCachedData,
setCachedData,
hasCache,
refetchQueries,
cancelQueries,
prefetchQuery
} from '@uecsio/query-cache'
// Get cached data
const userData = getCachedData(['users', 1])
// Check if data exists in cache
if (hasCache(['users', 1])) {
console.log('User data is cached')
}
// Set/update cached data (optimistic updates)
setCachedData(['users', 1], (oldData) => ({
...oldData,
name: 'New Name'
}))
// Force refetch
await refetchQueries(['users'])
// Cancel ongoing requests (prevent race conditions)
await cancelQueries(['users'])
// Prefetch data
await prefetchQuery(['users', 2], () => fetchUser(2))
```
```vue
<script setup>
import { getCachedData, setCachedData, invalidateCache } from '@uecsio/query-cache'
async function toggleUserStatus(userId) {
const queryKey = ['users']
// Save previous state
const previousData = getCachedData(queryKey)
// Optimistically update UI
setCachedData(queryKey, (old) => ({
...old,
data: old.data.map(user =>
user.id === userId
? { ...user, active: !user.active }
: user
)
}))
try {
// Make API call
await api.patch(`/users/${userId}/toggle-status`)
} catch (error) {
// Rollback on error
setCachedData(queryKey, previousData)
throw error
} finally {
// Refetch to ensure consistency
invalidateCache(queryKey)
}
}
</script>
```
```vue
<template>
<div>
<button @click="handleRefresh" :disabled="isRefreshing">
{{ isRefreshing ? 'Refreshing...' : 'Refresh Data' }}
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { invalidateCache } from '@uecsio/query-cache'
const isRefreshing = ref(false)
async function handleRefresh() {
isRefreshing.value = true
try {
await invalidateCache(['users'])
} finally {
isRefreshing.value = false
}
}
</script>
```
```javascript
import { clearAllCache } from '@uecsio/query-cache'
import { router } from './router'
function logout() {
// Clear authentication
localStorage.removeItem('token')
// Clear all cached data
clearAllCache()
// Redirect to login
router.push('/login')
}
```
Removes queries from cache completely. Next access will show loading state.
```javascript
clearCache(['users', 1]) // Clear specific query
clearCache(['users']) // Clear all matching queries
```
#### `invalidateCache(queryKey)`
Marks cache as stale and refetches if active. Shows old data while loading (better UX).
```javascript
invalidateCache(['users', 1]) // Invalidate specific query
invalidateCache(['users']) // Invalidate all matching queries
```
#### `clearAllCache()`
Clears ALL TanStack Query cache. Use with caution.
```javascript
clearAllCache()
```
### Data Access
#### `getCachedData<T>(queryKey): T | undefined`
Retrieves cached data without triggering a fetch.
```typescript
const user = getCachedData<User>(['users', 1])
```
Manually update cached data.
```typescript
// Set directly
setCachedData(['users', 1], newUser)
// Update function
setCachedData(['users', 1], (old) => ({ ...old, name: 'New Name' }))
```
Check if data exists in cache.
```javascript
if (hasCache(['users', 1])) {
// Data is cached
}
```
Force immediate refetch of queries.
```javascript
await refetchQueries(['users'])
await refetchQueries(['users'], { active: true }) // Only active queries
```
#### `resetQueries(queryKey, options?)`
Reset queries to initial state.
```javascript
resetQueries(['users'])
```
#### `cancelQueries(queryKey): Promise<void>`
Cancel ongoing requests to prevent race conditions.
```javascript
await cancelQueries(['users'])
```
#### `prefetchQuery(queryKey, queryFn, options?): Promise<void>`
Preload data before it's needed.
```javascript
await prefetchQuery(['users', 2], () => fetchUser(2))
```
Get query state information (loading, error, etc.).
```javascript
const state = getQueryState(['users', 1])
console.log(state.status) // 'loading', 'success', 'error'
```
Get direct access to TanStack Query client for advanced operations.
```javascript
const queryClient = getQueryClient()
queryClient.setDefaultOptions({ queries: { staleTime: 60000 } })
```
Query keys can be strings or arrays. Arrays allow hierarchical patterns:
```javascript
// Specific user
['users', 1]
// All users
['users']
// User with filters
['users', { role: 'admin' }]
// Paginated data
['users', { page: 1, limit: 10 }]
```
When clearing/invalidating with partial keys, all matching queries are affected:
```javascript
clearCache(['users']) // Clears ALL user-related queries
clearCache(['users', 1]) // Clears only user with ID 1
```
## Clear vs Invalidate
| Method | Behavior | Loading State | Best For |
|--------|----------|---------------|----------|
| `clearCache()` | Removes data completely | Shows loading spinner | Deleted records, major changes |
| `invalidateCache()` | Marks stale, refetches | Shows old data while loading | Updates, creates, refreshes |
**Recommendation:** Use `invalidateCache()` for most cases as it provides smoother UX.
## TypeScript Support
Full TypeScript support with proper types:
```typescript
import { getCachedData, setCachedData } from '@uecsio/query-cache'
interface User {
id: number
name: string
email: string
}
// Typed cache access
const user = getCachedData<User>(['users', 1])
// Typed cache updates
setCachedData<User>(['users', 1], (oldUser) => ({
...oldUser,
name: 'New Name'
}))
```
1. **Use specific query keys** - More granular control over cache
2. **Prefer invalidate over clear** - Better UX with background refetching
3. **Handle errors in optimistic updates** - Always have a rollback strategy
4. **Cancel queries on cleanup** - Prevent memory leaks and race conditions
5. **Prefetch predictable navigation** - Improve perceived performance
```javascript
// stores/users.js
import { defineStore } from 'pinia'
import { invalidateCache } from '@uecsio/query-cache'
export const useUsersStore = defineStore('users', () => {
async function createUser(data) {
await api.post('/users', data)
invalidateCache(['users'])
}
return { createUser }
})
```
```javascript
// router/index.js
import { clearAllCache } from '@uecsio/query-cache'
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
clearAllCache()
next('/login')
} else {
next()
}
})
```
MIT
Built on top of [TanStack Query](https://tanstack.com/query) (formerly React Query).