Provide a context object (client, queryKey, meta) to the placeholderData function #11140
Summary
Current signatureexport type PlaceholderDataFunction
TQueryFnData = unknown,
TError = DefaultError,
TQueryData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = (
previousData: TQueryData | undefined,
previousQuery: Query<TQueryFnData, TError, TQueryData, TQueryKey> | undefined,
) => TQueryData | undefinedProposalexport type PlaceholderDataContext<TQueryKey extends QueryKey = QueryKey> = {
client: QueryClient
queryKey: TQueryKey
meta: QueryMeta | undefined
}
export type PlaceholderDataFunction<...> = (
previousData: TQueryData | undefined,
previousQuery: Query<...> | undefined,
context: PlaceholderDataContext<TQueryKey>,
) => TQueryData | undefinedThe parameter is added at the end, so existing code keeps working. Why
The practical consequence is that the "placeholder data from cache" example in the docs can only live inside a component: function BlogPost({ blogPostId }) {
const queryClient = useQueryClient()
const result = useQuery({
queryKey: ['blogPost', blogPostId],
queryFn: () => fetch(`/blogPosts/${blogPostId}`),
placeholderData: () =>
queryClient
.getQueryData(['blogPosts'])
?.find((d) => d.id === blogPostId),
})
}That collides with the export const blogPostOptions = (client: QueryClient, blogPostId: string) =>
queryOptions({
queryKey: ['blogPost', blogPostId],
queryFn: () => fetchBlogPost(blogPostId),
placeholderData: () =>
client.getQueryData(['blogPosts'])?.find((d) => d.id === blogPostId),
})And that spreads. Every caller now needs a client just to build the options: components, With a context argument, the factory depends only on its own arguments again: export const blogPostOptions = (blogPostId: string) =>
queryOptions({
queryKey: ['blogPost', blogPostId],
queryFn: () => fetchBlogPost(blogPostId),
placeholderData: (_previousData, _previousQuery, { client }) =>
client.getQueryData(['blogPosts'])?.find((d) => d.id === blogPostId),
})Why the existing parameters don't cover this
#10427 is open and asks for the current query as a parameter for a related reason: a globally configured ImplementationThe call site in placeholderData =
typeof options.placeholderData === 'function'
? options.placeholderData(
this.#lastQueryWithDefinedData?.state.data,
this.#lastQueryWithDefinedData as any,
{
client: this.#client,
queryKey: options.queryKey,
meta: options.meta,
},
)
: options.placeholderDataI can send a PR with tests and a changeset if this direction makes sense. |
Replies: 1 comment
|
|
queryFnis the only one that receives aQueryFunctionContext, we can’t pass it everywhere because we don’t have the signal and consuming it opts you into query cancellation.placeholderDatais an observer specific property so it’s rarely among the shared query options. If you really want that, passing thequeryClientin manually to a function is fine imo.