Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 332x 332x 604x 604x 604x 332x 604x 332x 39x 1x 38x 332x 204x 24x 180x 180x 566x 20x 332x 8051x 7872x 23484x 8051x 23484x 8051x 8051x 8051x 8051x 1313x 857x 1313x 2x 7706x | import type React from "react";
import type { QueryKey } from "@tanstack/react-query";
import { useQueryClient } from "@tanstack/react-query";
import { useRouterState } from "@tanstack/react-router";
import { TRPCClientError } from "@trpc/client";
import type { ResolverDef, TRPCQueryOptions } from "@trpc/tanstack-react-query";
import { isNonNullish } from "remeda";
import { useBooleanState } from "~app/hooks/use-boolean-state";
import { useMountEffect } from "~app/hooks/use-mount-effect";
import { transformer } from "~utils/transformer";
import type { RouterContext } from "~web/pages/__root";
// see https://github.com/TanStack/router/issues/4084
const ERROR_TAG = "__error__";
const getPrefetchPromise = async (
ctx: PrefetchContext,
options: ReturnType<TRPCQueryOptions<ResolverDef>>,
) => {
try {
const query = (await ctx.context.queryClient.fetchQuery(
options,
)) as unknown;
return transformer.serialize(query);
} catch (error) {
return { [ERROR_TAG]: true, message: String(error) };
}
};
type PrefetchContext = { context: RouterContext };
const prefetchQuery = (
ctx: PrefetchContext,
options: ReturnType<TRPCQueryOptions<ResolverDef>>,
) => ({
queryKey: options.queryKey,
promise: getPrefetchPromise(ctx, options),
});
export const prefetchQueries = (
ctx: PrefetchContext,
...optionsSet: ReturnType<TRPCQueryOptions<ResolverDef>>[]
) => {
Eif (!import.meta.env.SSR) {
return [];
}
return optionsSet.map((options) => prefetchQuery(ctx, options));
};
class SerializedTRPCError extends Error {
public queryKey: QueryKey;
public errorObject: Error;
public constructor(queryKey: QueryKey, errorObject: Error) {
super(errorObject.message);
this.name = "SerializedTRPCError";
this.queryKey = queryKey;
this.errorObject = errorObject;
}
}
export const prefetchQueriesWith = async <T,>(
ctx: PrefetchContext,
getPromise: () => Promise<T>,
getOptions: (result: T) => ReturnType<TRPCQueryOptions<ResolverDef>>[],
) => {
Eif (!import.meta.env.SSR) {
return [];
}
try {
const result = await getPromise();
return getOptions(result).map((options) => prefetchQuery(ctx, options));
} catch {
return [];
}
};
export const HydrationBoundary: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const prefetchedQueries = useRouterState({
select: (state) =>
state.matches.flatMap((match) =>
typeof match.loaderData === "object" && "prefetched" in match.loaderData
? match.loaderData.prefetched
: [],
),
});
const matchErrors = useRouterState({
select: (state) =>
state.matches.map((match) => match.error).filter(isNonNullish),
});
const localQueryClient = useQueryClient();
const [isMounted, { setTrue: setMounted }] = useBooleanState();
useMountEffect(setMounted);
if (!isMounted) {
for (const query of prefetchedQueries) {
void localQueryClient.prefetchQuery({
queryKey: query.queryKey,
queryFn: async () => {
const result = await query.promise;
if (ERROR_TAG in result) {
throw new TRPCClientError(result.message);
}
return transformer.deserialize(result);
},
});
}
for (const error of matchErrors) {
Iif ("errorObject" in error) {
const castedError = error as SerializedTRPCError;
const serializedError = new SerializedTRPCError(
castedError.queryKey,
castedError.errorObject,
);
void localQueryClient.prefetchQuery({
queryKey: serializedError.queryKey,
queryFn: () => {
throw serializedError.errorObject;
},
});
}
}
}
return <>{children}</>;
};
|