UNPKG

@uecsio/query-cache

Version:

Cache management utilities for TanStack Query (Vue Query)

424 lines (313 loc) â€Ē 9.02 kB
# @uecsio/query-cache A lightweight utility library for managing [TanStack Query](https://tanstack.com/query) (Vue Query) cache in Vue 3 applications. ## Features - ðŸŽŊ **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 ## Installation ```bash npm install @uecsio/query-cache ``` **Peer Dependencies:** ```bash npm install vue @tanstack/vue-query ``` ## Setup 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') ``` ## Usage ### Basic Cache Operations ```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() ``` ### After Data Mutations ```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> ``` ### Advanced Operations ```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)) ``` ### Optimistic Updates Example ```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> ``` ### Refresh Button ```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> ``` ### On User Logout ```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') } ``` ## API Reference ### Core Functions #### `clearCache(queryKey)` 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]) ``` #### `setCachedData<T>(queryKey, data | updater)` Manually update cached data. ```typescript // Set directly setCachedData(['users', 1], newUser) // Update function setCachedData(['users', 1], (old) => ({ ...old, name: 'New Name' })) ``` #### `hasCache(queryKey): boolean` Check if data exists in cache. ```javascript if (hasCache(['users', 1])) { // Data is cached } ``` ### Advanced Functions #### `refetchQueries(queryKey, options?): Promise<void>` 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)) ``` #### `getQueryState(queryKey)` Get query state information (loading, error, etc.). ```javascript const state = getQueryState(['users', 1]) console.log(state.status) // 'loading', 'success', 'error' ``` #### `getQueryClient(): QueryClient` Get direct access to TanStack Query client for advanced operations. ```javascript const queryClient = getQueryClient() queryClient.setDefaultOptions({ queries: { staleTime: 60000 } }) ``` ## Query Key Patterns 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' })) ``` ## Best Practices 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 ## Integration Examples ### With Pinia Store ```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 } }) ``` ### With Vue Router ```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() } }) ``` ## License MIT ## Credits Built on top of [TanStack Query](https://tanstack.com/query) (formerly React Query).