@wisemen/vue-core-api-utils
Version:
158 lines (113 loc) • 4.99 kB
Markdown
---
name: cache-management
description: >
Type-safe QueryClient with get/set/update/invalidate methods, predicate-based updates, cascade invalidation strategy, shared cache across components, lazy refetch patterns.
type: core
library: vue-core-api-utils
---
Manually read, write, update, and invalidate the query cache using the type-safe `QueryClient` wrapper. This is useful for optimistic updates and strategically invalidating affected queries.
```typescript
import { useQueryClient } from '@/api'
const queryClient = useQueryClient()
// Get cached data
const contact = queryClient.get(['contactDetail', { contactUuid: '123' }])
// Set cached data
queryClient.set(
['contactDetail', { contactUuid: '123' }],
updatedContact
)
// Update cached data with a predicate (returns { rollback } for reverting)
const { rollback } = queryClient.update('contactList', {
by: (contact) => contact.id === '123',
value: (contact) => ({ ...contact, name: 'Updated' }),
})
// Invalidate queries (async — triggers refetch)
await queryClient.invalidate('contactList')
```
`useQueryClient()` is a helper you create in your `@/api` module (see [getting-started](../getting-started/SKILL.md)) that wraps `new QueryClient(getTanstackQueryClient())`.
```typescript
const queryClient = useQueryClient()
// Get specific query
const contact = queryClient.get(
['contactDetail', { contactUuid: '123' }]
)
// Get all queries with a key
const allContacts = queryClient.get('contactList')
// Get exact query only
const specificQuery = queryClient.get('contactList', { isExact: true })
```
Returns the cached data or null if not cached. The QueryClient infers entity type from your query key definition.
```typescript
const queryClient = useQueryClient()
queryClient.set(
['contactDetail', { contactUuid: '123' }],
{ id: '123', name: 'John', email: 'john@email.com' }
)
// For lists, set works with arrays too
queryClient.set('contactList', [
{ id: '123', name: 'John' },
{ id: '456', name: 'Jane' },
])
```
`set()` replaces all cached data for that query key.
```typescript
const queryClient = useQueryClient()
// Update a single item in a list — returns { rollback } for reverting
const { rollback } = queryClient.update('contactList', {
by: (contact) => contact.id === '123', // Predicate
value: (contact) => ({ // Transform
...contact,
name: 'Updated John'
}),
})
// For single entities, the predicate always matches
const { rollback: rollbackDetail } = queryClient.update('contactDetail', {
by: (contact) => true,
value: (contact) => ({ ...contact, name: 'Updated' }),
})
```
`update()` returns `{ rollback }` — a function that reverts the cache to its previous state. Use this for optimistic updates. QueryClient knows whether the entity is an array or single item, so predicates work transparently on lists.
```typescript
const queryClient = useQueryClient()
// Invalidate all queries with this key
await queryClient.invalidate('contactList')
// Invalidate specific query
await queryClient.invalidate(['contactDetail', { contactUuid: '123' }])
// After invalidation, the next query interaction triggers a refetch
```
Invalidation marks cached data as stale. The next interaction (component mount, user action) triggers a refetch.
## Cache Strategy
> Explicitly invalidate only the queries affected by the mutation. Let lazy refetch handle the rest when users navigate to pages needing other data.
>
> — Maintainer guidance
When a mutation succeeds, look at what changed:
- If you updated a contact, invalidate `contactDetail` and `contactList` (they both show that contact)
- If you archived a conversation, invalidate `conversationList` (but maybe not `conversationDetail` unless showing the one you archived)
- Don't invalidate unrelated queries — let them refetch lazily when needed
## Shared Cache Across Components
Important: Multiple components using the same query key share the same cached data. This is a feature, not a bug.
```typescript
// ComponentA
const { result: resultA } = useQuery('userDetail', {
params: { id: computed(() => 'same-id') },
queryFn: () => UserService.getById('same-id'),
})
// ComponentB
const { result: resultB } = useQuery('userDetail', {
params: { id: computed(() => 'same-id') },
queryFn: () => UserService.getById('same-id'),
})
// resultA and resultB are the SAME cached value
// Mutation in B invalidates A's cache
```
Use this to your advantage: invalidate a query and all components using it refetch automatically.
- [Writing Mutations](../writing-mutations/SKILL.md) — Every mutation needs to know which queries to invalidate
- [Writing Queries](../writing-queries/SKILL.md) — Understanding caching strategy informs cache management choices