Docs: https://tanstack.com/query/latest/docs/framework/react/guides/initial-query-data
Advanced explanation (examples are partially taken from there): https://tkdodo.eu/blog/seeding-the-query-cache#seeding-details-from-lists
- You can pull the details from the list
const useTodo = (id: number) => {
const queryClient = useQueryClient()
return useQuery({
queryKey: ['todos', 'detail', id],
queryFn: () => fetchTodo(id),
initialData: () => {
return queryClient
.getQueryData(['todos', 'list'])
?.find((todo) => todo.id === id)
},
// It will tell React Query when the data we are passing in as initialData
// was originally fetched, so it can determine staleness correctly
initialDataUpdatedAt: () =>
// ⬇️ get the last fetch time of the list
queryClient.getQueryState(['todos', 'list'])?.dataUpdatedAt,
})
}
- Alternatively you can create initialData for all your per-item queries when you fetch the list like this:
const useTodosList = () => {
const queryClient = useQueryClient()
return useQuery({
queryKey: ['todos', 'list'],
queryFn: async () => {
const todos = await fetchTodos()
todos.forEach((todo) => {
// ⬇️ create a detail cache for each item
queryClient.setQueryData(['todos', 'detail', todo.id], todo)
})
return todos
},
})
}
const useTodo = (id: number) => {
const queryClient = useQueryClient()
return useQuery({
queryKey: ['todos', 'detail', id],
queryFn: () => fetchTodo(id),
// ➡️ no need to set initial data manually as it is pushed by the list
})
}
Keep in mind that both approaches only work well if the structure of your detail query is exactly the same (or at least assignable to) the structure of the list query. If the detail view has a mandatory field that doesn't exist in the list, seeding via initialData is not a good idea. This is where placeholderData comes in, and I've written a comparison about the two in #9: Placeholder and Initial Data in React Query.
From https://tkdodo.eu/blog/seeding-the-query-cache#seeding-details-from-lists