QueryKeys
The QueryKeys class manages React Query keys and provides methods for cache operations like invalidation, refetching, and data retrieval. It's automatically created and used internally by QueryManager.
When to use QueryKeys
Use QueryKeys when the problem is query coordination, not direct cached item mutation.
Good fits:
- invalidate after a mutation
- refetch stale resource data
- cancel in-flight queries before optimistic work
- prefetch likely next data
- inspect cached query data
- remove cached query entries
If the real goal is to edit cached resource items directly, prefer Mutations.
QueryKeys vs Mutations
Use QueryKeys for query lifecycle operations.
Use Mutations for cached resource payload changes.
Typical split:
queryKeys.invalidateList(...)when the list should be refreshedmutations.updateItems(...)when you already know how the cached item changed
Many flows intentionally use both.
Key Generation
The QueryKeys class generates consistent query keys for different operations:
// Internal key structure
{
list: ['users', 'list'],
retrieve: (id) => ['users', 'retrieve', id],
create: ['users', 'create'],
update: ['users', 'update'],
delete: ['users', 'delete']
}
List Keys with Filters
List query keys automatically include filters when present:
// Without filters: ['users', 'list']
// With filters: ['users', 'list', { status: 'active', search: 'john' }]
const queryKey = queryKeys.listKeyWithFilters({ status: 'active' })
// Result: ['users', 'list', { status: 'active' }]
Cache Operations
Invalidation
// Access QueryKeys from QueryManager instance
const { queryKeys } = userQueryManager
// Invalidate all queries for this resource
await queryKeys.invalidateAll()
// Invalidate all list queries
await queryKeys.invalidateList()
// Invalidate list queries with specific filters
await queryKeys.invalidateList({ status: 'active' })
// Invalidate specific retrieve query
await queryKeys.invalidateRetrieve('user-123')
// Invalidate with options
await queryKeys.invalidateAll(
{ exact: true },
{ cancelRefetch: false }
)
Refetching
// Refetch all queries
await queryKeys.refetchAll()
// Refetch list queries
await queryKeys.refetchList()
// Refetch list queries with filters, ignoring specific query keys
await queryKeys.refetchList(
{ status: 'active' }, // filters
[['users', 'list', { status: 'inactive' }]] // ignore these keys
)
// Refetch specific retrieve query
await queryKeys.refetchRetrieve('user-123')
Data Retrieval
// Get list data from cache
const { items, itemMap } = queryKeys.getListData()
console.log('Cached users:', items)
console.log('User map:', itemMap['user-123'])
// Get list data with filters
const { items: activeUsers } = queryKeys.getListData({ status: 'active' })
// Get retrieve data from cache
const user = queryKeys.getRetrieveData('user-123')
console.log('Cached user:', user)
// Get retrieve data only from specific query (not from list cache)
const userFromQuery = queryKeys.getRetrieveData('user-123', true)
Query Management
// Cancel in-flight list queries
await queryKeys.cancelListQueries()
// Cancel specific list queries with filters
await queryKeys.cancelListQueries({ status: 'active' })
// Remove query data from cache
await queryKeys.removeRetrieveQueryData('user-123')
// Get all list queries
const listQueries = queryKeys.getAllListQueries()
console.log('Active list queries:', listQueries.length)
Practical Examples
Smart Cache Invalidation
function useUserActions() {
const { queryKeys } = userQueryManager
const handleUserStatusChange = async (userId: string) => {
// Invalidate the specific user
await queryKeys.invalidateRetrieve(userId)
// Invalidate all list queries since status affects filtering
await queryKeys.invalidateList()
}
const handleUserRoleChange = async (userId: string) => {
// Only invalidate lists that might be affected by role changes
await queryKeys.invalidateRetrieve(userId)
await queryKeys.invalidateList({ role: 'admin' })
await queryKeys.invalidateList({ role: 'user' })
}
return { handleUserStatusChange, handleUserRoleChange }
}
Background Refetching
function useBackgroundSync() {
const { queryKeys } = userQueryManager
useEffect(() => {
const interval = setInterval(async () => {
// Refetch stale data in background
await queryKeys.refetchAll(
{ type: 'active' }, // only active queries
{ cancelRefetch: false } // don't cancel ongoing requests
)
}, 5 * 60 * 1000) // every 5 minutes
return () => clearInterval(interval)
}, [])
}