Skip to main content

Practical Examples

QueryManager plus QueryOperations

A common production pattern is:

  1. create one manager for the resource
  2. create operations for extra action-style endpoints
  3. use manager queryKeys and mutations to coordinate cache

The action should not necessarily become its own CRUD manager.

Prefetching

function UserCard({ user }: { user: User }) {
const handleMouseEnter = () => {
// Prefetch user details when hovering
userQueryManager.prefetchRetrieve(user.id, {
staleTime: 5 * 60 * 1000
})
}

return (
<div onMouseEnter={handleMouseEnter}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
)
}

Custom Optimistic Update Pattern

function useOptimisticUserUpdate() {
const { mutations, queryKeys } = userQueryManager

const optimisticUpdate = async (userId: string, updates: Partial<User>) => {
// Cancel ongoing queries
await queryKeys.cancelListQueries()

// Get current data
const previousUser = queryKeys.getRetrieveData(userId)
if (!previousUser) return null

// Apply optimistic update
const optimisticUser = { ...previousUser, ...updates }
mutations.updateItems(optimisticUser)

return {
rollback: () => mutations.updateItems(previousUser),
optimisticUser
}
}

return { optimisticUpdate }
}

Action mutation beside a resource manager

const postsManager = createQueryManager<Post>({
name: 'posts',
queryClient,
// listFn, retrieveFn, createFn, updateFn, deleteFn...
})

const postOperations = createQueryOperations({ queryClient })
.mutation('favorite', async (id: number) => api.post(`/posts/${id}/favorite/`))

async function favoritePost(id: number) {
const currentPost = postsManager.queryKeys.getRetrieveData(id)

if (currentPost) {
postsManager.mutations.updateItems({
...currentPost,
is_favorite: true,
})
}

await postOperations.mutations.favorite(id)
}