Aborting Procedure Calls
Cancel in-flight requests when a component unmounts with the abortOnUnmount option on a query
By default, tRPC doesn't cancel a request when the component that made it unmounts. To cancel it, set abortOnUnmount in the trpc option of the query:
pages/posts/[id].tsx
import { const skipToken: typeof skipTokenskipToken, function useQuery<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = readonly unknown[]>(options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): DefinedUseQueryResult<TData, TError> (+2 overloads)useQuery } from '@tanstack/react-query';
import { function useRouter(): NextRouteruseRouter } from 'next/router';
import { const useTRPC: () => TRPCOptionsProxy<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>, {
...;
}>
useTRPC } from '../../utils/trpc';
export default function function PostViewPage(): JSX.ElementPostViewPage() {
const const router: NextRouterrouter = function useRouter(): NextRouteruseRouter();
// `query.id` is undefined until the router is ready
const const id: string | string[] | undefinedid = const router: NextRouterrouter.query: ParsedUrlQueryquery.string | string[] | undefinedid;
const const trpc: TRPCOptionsProxy<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>, {
...;
}>
trpc = function useTRPC(): TRPCOptionsProxy<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>, {
...;
}>
useTRPC();
const const postQuery: UseQueryResult<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>>
postQuery = useQuery<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>, {
id: string;
title: string;
}, TRPCQueryKeyWithoutPrefix>(options: UndefinedInitialDataOptions<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>, {
id: string;
title: string;
}, TRPCQueryKeyWithoutPrefix>, queryClient?: QueryClient): UseQueryResult<...> (+2 overloads)
useQuery(
const trpc: TRPCOptionsProxy<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>, {
...;
}>
trpc.post: DecoratedRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>, {
...;
}> & DecorateRouterKeyable<...> & DecorateHandleReaders<...>
post.byId: DecorateQueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
transformer: false;
errorShape: DefaultErrorShape;
featureFlags: {
keyPrefix: false;
};
}> & Record<string, never>
byId.DecorateQueryProcedure<{ input: { id: string; }; output: { id: string; title: string; }; transformer: false; errorShape: DefaultErrorShape; featureFlags: { keyPrefix: false; }; }>.queryOptions: TRPCQueryOptions
<{
id: string;
title: string;
}, {
id: string;
title: string;
}>(input: typeof skipToken | {
id: string;
}, opts?: UndefinedTRPCQueryOptionsIn<{
id: string;
title: string;
}, {
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>, {
keyPrefix: false;
}> | undefined) => UndefinedTRPCQueryOptionsOut<{
id: string;
title: string;
}, {
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>, {
keyPrefix: false;
}> (+2 overloads)
queryOptions(typeof const id: string | string[] | undefinedid === 'string' ? { id: stringid } : const skipToken: typeof skipTokenskipToken, {
TRPCQueryBaseOptions.trpc?: TRPCReactRequestOptions | undefinedtrpc: { TRPCReactRequestOptions.abortOnUnmount?: boolean | undefinedabortOnUnmount: true },
}),
);
switch (const postQuery: UseQueryResult<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>>
postQuery.status: "pending" | "error" | "success"status) {
case 'pending':
return <JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Loading...</JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>;
case 'error':
return <JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Error: {const postQuery: QueryObserverRefetchErrorResult<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>> | QueryObserverLoadingErrorResult<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>>
postQuery.error: TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>
error.TRPCClientErrorBase<TShape extends DefaultErrorShape>.message: stringmessage}</JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>;
case 'success':
return <JSX.IntrinsicElements.h1: DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>{const postQuery: QueryObserverSuccessResult<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>> | QueryObserverPlaceholderResult<{
id: string;
title: string;
}, TRPCClientErrorLike<{
transformer: false;
errorShape: DefaultErrorShape;
}>>
postQuery.data: {
id: string;
title: string;
}
data.title: stringtitle}</JSX.IntrinsicElements.h1: DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>;
}
}There's no global switch: createTRPCContext() takes no options, so set it on each query that needs it. It works for queryOptions and infiniteQueryOptions, since TanStack React Query only cancels queries.
With httpBatchLink or httpBatchStreamLink, the HTTP request only stops once every query in its batch is cancelled. Cancelling one query alone just drops its result. To stop the request for a single query, send it through httpLink, for instance with a splitLink.