T
TanStack2mo ago
rival-black

non serializable objects in Route Context

I have an API client that is required on the client and on the server. All examples I see for tanstack just initializes these things in a global way, but that doesn't work when you need to pass arguments. I have a few config variables that I fetch from the server and with that I can initialize the client. I do this in the root route beforeLoad
beforeLoad: async ({context: {queryClient}}) => {
const data = await queryClient.fetchQuery({
queryKey: [QueryKey.CONFIG],
queryFn: fetchConfigFromServer,
});
const fetchClient = createOpenApiFetchClient<paths>({baseUrl: data.CONTENT_API_BASE_URL});
return {
$contentApi: createOpenApiClient(fetchClient),
cfg: data,
};
},
beforeLoad: async ({context: {queryClient}}) => {
const data = await queryClient.fetchQuery({
queryKey: [QueryKey.CONFIG],
queryFn: fetchConfigFromServer,
});
const fetchClient = createOpenApiFetchClient<paths>({baseUrl: data.CONTENT_API_BASE_URL});
return {
$contentApi: createOpenApiClient(fetchClient),
cfg: data,
};
},
Now any route can use that client like so:
loader: ({context: {$contentApi, queryClient}, params: {csin}}) => {
return queryClient.ensureQueryData($contentApi.queryOptions("get", "/product-detail/v2/{csins}", {params: {path: {csins: csin}}}));
},
loader: ({context: {$contentApi, queryClient}, params: {csin}}) => {
return queryClient.ensureQueryData($contentApi.queryOptions("get", "/product-detail/v2/{csins}", {params: {path: {csins: csin}}}));
},
Now I wanna use that in component.
const {$contentApi} = RootRoute.useRouteContext();
const {data, isError, isLoading} = useQuery($contentApi.queryOptions("get", "/customer-review"));
const {$contentApi} = RootRoute.useRouteContext();
const {data, isError, isLoading} = useQuery($contentApi.queryOptions("get", "/customer-review"));
Problem is this won't work, because $contentApi is no longer the real client, it has been serialized/deserialized I think.
(intermediate value)().queryOptions is not a function
My hack around this currently is re-creating the api client in a context and using the context in the component instead. But loaders now use the route context and components need to use the context, that's odd to me. Is there a better way?
const fetchClient = createOpenApiFetchClient<paths>({baseUrl: config.CONTENT_API_BASE_URL});
const $contentApi = createOpenApiClient(fetchClient);
const contextValue: ApiContextType = {
$contentApi,
};

return <ApiContext.Provider value={contextValue}>{children}</ApiContext.Provider>;
const fetchClient = createOpenApiFetchClient<paths>({baseUrl: config.CONTENT_API_BASE_URL});
const $contentApi = createOpenApiClient(fetchClient);
const contextValue: ApiContextType = {
$contentApi,
};

return <ApiContext.Provider value={contextValue}>{children}</ApiContext.Provider>;
1 Reply
exotic-emerald
exotic-emerald2mo ago
we will be adding a way to opt out of serialization and then rerun it on the client

Did you find this page helpful?