Skip to content

Commit

Permalink
added dev tools for tanstack/query
Browse files Browse the repository at this point in the history
  • Loading branch information
ejmg committed Jul 12, 2024
1 parent a12286c commit a0e39fa
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 8 deletions.
44 changes: 37 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@next/eslint-plugin-next": "^14.2.3",
"@pgtyped/cli": "^2.3.0",
"@stylistic/eslint-plugin": "^2.1.0",
"@tanstack/react-query-devtools": "^5.51.1",
"@types/eslint": "^8.56.10",
"@types/node": "^20",
"@types/react": "^18",
Expand Down
2 changes: 2 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cn } from "@/lib/utils";
import "@/lib/patch-toJSON-BigInt";
import Navbar from "@/components/Navbar";
import Providers from "@/components/Providers";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { CodeIcon } from "lucide-react";
import Link from "next/link";

Expand Down Expand Up @@ -41,6 +42,7 @@ export default function RootLayout({
</Link>
</div>
</div>
<ReactQueryDevtools initialIsOpen={false} />
</Providers>
</body>
</html>
Expand Down
69 changes: 68 additions & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,73 @@
import { toast } from "@/components/ui/use-toast";
import { QueryCache, QueryClient, defaultShouldDehydrateQuery, isServer } from "@tanstack/react-query";
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

let browserQueryClient: QueryClient | undefined = undefined;

const makeQueryClient = () => {
// const { toast } = useToast();
return new QueryClient({
defaultOptions: {
queries: {
// Direct suggestion by tanstack, to prevent over-eager refetching from the client.
staleTime: 60 * 1000,
},
dehydrate: {
// only successful and pending Queries are included per defaults
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === "pending",
},
},
queryCache: new QueryCache({
onError: (error, query) => {
// TODO: Overall model is fine but need to change how meta is used.
// Idea: Add a `errorTitle` field instead that can be used in place of "Error" below. This gives a top level, succinct explanation.
// `description` becomes whatever value we store inside our error value. This is what needs to be refactored to make all queries play nicely.
// This allows each error to clearly signal its nature while also giving detail where appropriate. The issue of that detail is delegated to the useQuery callsite
// and any component/route that throws errors.
// There may be a more elegant way of expressing this but the general typing of onError's `error` and `query` arguments requires some amount of refinement for safety.
// https://tanstack.com/query/latest/docs/react/reference/QueryCache
let errorMessage = "";
const meta = query?.meta ?? undefined ;
if (meta) {
// Precondition for this type cast: meta is of type Record<string, unknown> where any query with a meta object containing the property `errorMessage` has a value of type string.
errorMessage = meta?.errorMessage as string ?? "";
}
if (errorMessage !== "") {
toast({
variant: "destructive",
title: "Error",
description: `${errorMessage}`,
});
} else {
// TODO: Realistically, this will not be a useful error and should be improved further.
toast({
variant: "destructive",
title: "Error",
description: `${error.message}`,
});
}
},
}),
});
};

export const getQueryClient = () => {
if (isServer) {
// Server: always make a new query client
return makeQueryClient();
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
};

0 comments on commit a0e39fa

Please sign in to comment.