T
TanStack4w ago
subsequent-cyan

invalidate not rerunning loader

I'm having a weird issue, all the information i've found suggests it should work but it doesn't. I want to run the loader using router.invalidate() but I can't get it to actually run the query again. The following page loads data and sends it to the form component, once the user saves some data I want to invalidate the data so it's reloaded. I do this as suggested by router.invalidate() from useRouter() but nothing happens.
import SchemaFormLoading from '@/components/SchemaForm/components/SchemaFormLoading';
import SchemaForm from '@/components/SchemaForm/SchemaForm';
import { newRegisterEntityToEntityName } from '@/queries/schemaform/useFetchSchema';
import fetchFormLegacyOptions from '@/queries/schemaform/useFetchSchemaLegacy';
import { registerPermissionCheck } from '@/utils/authenticationUtils';
import { useSuspenseQuery } from '@tanstack/react-query';
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/_auth/omsorgsregistret/verksamhet/$id')({
beforeLoad: () => {
registerPermissionCheck({ register: 'omsorgsregistret', entity: 'verksamhet' });
},
component: RouteComponent,
validateSearch: (search) =>
search as {
id?: string;
newObject?: string;
options?: string;
newOrg?: boolean;
newOperation?: boolean;
},
loaderDeps: ({ search: { id, newObject, options, newOrg, newOperation } }) => ({
id,
newObject,
options,
newOrg,
newOperation
}),
context: ({ params, deps }) => ({
queryOptions: fetchFormLegacyOptions({
entityName: newRegisterEntityToEntityName('omsorgsregistret', 'verksamhet')!,
...deps,
id: params.id
})
}),
loader: async ({ context, params, deps }) => {
console.log('loader running');
const data = await context.queryClient.ensureQueryData(context.queryOptions);

return {
breadcrumb: data?.formData?.Name || params.id,
data,
props: {
formId: params.id,
formName: context.queryOptions.queryKey[1],
...deps
}
};
},
pendingComponent: () => <SchemaFormLoading />
});

function RouteComponent() {
const context = Route.useRouteContext();
const { props } = Route.useLoaderData();
const { data } = useSuspenseQuery(context.queryOptions) as any;

// useRouter().invalidate() is called in this component to refetch data after it's saved
// but loader is never run again
return <SchemaForm {...props} data={data} />;
}
import SchemaFormLoading from '@/components/SchemaForm/components/SchemaFormLoading';
import SchemaForm from '@/components/SchemaForm/SchemaForm';
import { newRegisterEntityToEntityName } from '@/queries/schemaform/useFetchSchema';
import fetchFormLegacyOptions from '@/queries/schemaform/useFetchSchemaLegacy';
import { registerPermissionCheck } from '@/utils/authenticationUtils';
import { useSuspenseQuery } from '@tanstack/react-query';
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/_auth/omsorgsregistret/verksamhet/$id')({
beforeLoad: () => {
registerPermissionCheck({ register: 'omsorgsregistret', entity: 'verksamhet' });
},
component: RouteComponent,
validateSearch: (search) =>
search as {
id?: string;
newObject?: string;
options?: string;
newOrg?: boolean;
newOperation?: boolean;
},
loaderDeps: ({ search: { id, newObject, options, newOrg, newOperation } }) => ({
id,
newObject,
options,
newOrg,
newOperation
}),
context: ({ params, deps }) => ({
queryOptions: fetchFormLegacyOptions({
entityName: newRegisterEntityToEntityName('omsorgsregistret', 'verksamhet')!,
...deps,
id: params.id
})
}),
loader: async ({ context, params, deps }) => {
console.log('loader running');
const data = await context.queryClient.ensureQueryData(context.queryOptions);

return {
breadcrumb: data?.formData?.Name || params.id,
data,
props: {
formId: params.id,
formName: context.queryOptions.queryKey[1],
...deps
}
};
},
pendingComponent: () => <SchemaFormLoading />
});

function RouteComponent() {
const context = Route.useRouteContext();
const { props } = Route.useLoaderData();
const { data } = useSuspenseQuery(context.queryOptions) as any;

// useRouter().invalidate() is called in this component to refetch data after it's saved
// but loader is never run again
return <SchemaForm {...props} data={data} />;
}
const fetchFormLegacyOptions = (props: FetchLegacyFormParams) => {
return queryOptions<any, AxiosError, any, QueryKeys>({
queryKey: ['legacySchemaForm', props.entityName, props.id],
queryFn: () => fetchSchemaQuery(props),
enabled: true
});
};
const fetchFormLegacyOptions = (props: FetchLegacyFormParams) => {
return queryOptions<any, AxiosError, any, QueryKeys>({
queryKey: ['legacySchemaForm', props.entityName, props.id],
queryFn: () => fetchSchemaQuery(props),
enabled: true
});
};
2 Replies
typical-coral
typical-coral3w ago
just scrolling through after posting a question...i think you can only pass functions as context at the createRouter level and not on the file route level. Passing funcs used to be a thing but I think that changed recently and it's now reserved for serializable data only. Maybe thats why?
extended-salmon
extended-salmon3w ago
I'm just looking and, this is more work in the router's setup functions than I'm used to but it seems to me like you are invalidating the ROUTER but not the queryClient, so the ensureQueryData comes through a second time and it says "well I have data, so I will return it synchronously without re-running the queryFn" -- unless you set that query's staleTime very low, or change it to fetchQuery. Or, I think the best solution here is to just invalidate the cache key instead of the router! Because you have set up a reactive listener in the RouteComponent (with useSuspenseQuery), invalidating the query will re-run the query function even without the router's loader firing. I see you have a console.log in there -- can you see if it's running when you expect it to? (Either way, it still seems like invalidating the query is the right approach.)

Did you find this page helpful?