From 1f38b3113db1bc2317d491ab36fe764485667960 Mon Sep 17 00:00:00 2001 From: ejmg Date: Thu, 11 Jul 2024 22:54:31 -0500 Subject: [PATCH] FIX: turns out tanstack docs suck and break your code --- src/app/blocks/page.tsx | 5 +- src/app/transaction/[hash]/page.tsx | 61 ++++++----------- src/components/Providers/index.tsx | 69 +------------------- src/components/Transaction/getTransaction.ts | 14 ++++ src/components/Transaction/index.tsx | 23 ++++--- 5 files changed, 53 insertions(+), 119 deletions(-) create mode 100644 src/components/Transaction/getTransaction.ts diff --git a/src/app/blocks/page.tsx b/src/app/blocks/page.tsx index c526f1c..a4df102 100644 --- a/src/app/blocks/page.tsx +++ b/src/app/blocks/page.tsx @@ -1,11 +1,12 @@ import { BlocksTable } from "@/components/BlocksTable"; import getBlocks from "@/components/BlocksTable/getBlocks"; -import { HydrationBoundary, QueryClient, dehydrate } from "@tanstack/react-query"; +import { getQueryClient } from "@/lib/utils"; +import { HydrationBoundary, dehydrate } from "@tanstack/react-query"; export const dynamic = "force-dynamic"; const Page = () => { - const queryClient = new QueryClient(); + const queryClient = getQueryClient(); const defaultQueryOptions = { pageIndex: 0, diff --git a/src/app/transaction/[hash]/page.tsx b/src/app/transaction/[hash]/page.tsx index fd9a9b6..0b09eb2 100644 --- a/src/app/transaction/[hash]/page.tsx +++ b/src/app/transaction/[hash]/page.tsx @@ -1,10 +1,9 @@ -"use client"; - +// "use client"; import { type FC } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { TransactionResult } from "@/lib/validators/search"; -import axios from "axios"; -import Transaction from "@/components/Transaction"; +import { HydrationBoundary, dehydrate } from "@tanstack/react-query"; +import { Transaction } from "@/components/Transaction"; +import getTransaction from "@/components/Transaction/getTransaction"; +import { getQueryClient } from "@/lib/utils"; interface PageProps { params: { @@ -12,51 +11,31 @@ interface PageProps { } } -// TODO: this entire page could probably be rendered statically on the server via layout.ts & some minor optimization via tanstack query context. const Page : FC = ({ params }) => { const { hash } = params; - const { data: txData , isError } = useQuery({ - queryFn: async () => { - console.log(`Fetching: GET /api/transaction?q=${hash}`); - const { data } = await axios.get(`/api/transaction?q=${hash}`); - console.log("Fetched result:", data); - const result = TransactionResult.safeParse(data); + const queryClient = getQueryClient(); - if (result.success) { - return result.data; - } else { - throw new Error(result.error.message); - } - }, - queryKey: ["txQuery", hash], - retry: false, + const endpoint = "/api/transaction/"; + const queryName = "txQuery"; + const errorMessage = "Failed to query transaction, please try reloading the page."; + + queryClient.prefetchQuery({ + queryKey: [queryName, hash], + queryFn: () => getTransaction({ endpoint, hash }), meta: { - errorMessage: "Failed to find transaction event with provided hash. Please check hash or try a different query.", + errorMessage, }, }); - if (isError) { - return ( -
-

No results found.

-
- ); - } - - // TODO: Replace with data table component views once those are fleshed out. - // TODO: add Suspense return ( -
- {txData ? ( -
-

Transaction Event Summary

-
- -
+
+

Transaction Summary

+ +
+
- ) :

No results

- } +
); }; diff --git a/src/components/Providers/index.tsx b/src/components/Providers/index.tsx index 9f16541..7fed8f6 100644 --- a/src/components/Providers/index.tsx +++ b/src/components/Providers/index.tsx @@ -1,13 +1,13 @@ "use client"; import React, { useState } from "react"; -import { QueryCache, QueryClient, QueryClientProvider, defaultShouldDehydrateQuery, isServer } from "@tanstack/react-query"; +import { QueryClientProvider } from "@tanstack/react-query"; import { createGrpcWebTransport } from "@connectrpc/connect-web"; import { TransportProvider } from "@connectrpc/connect-query"; import { Toaster } from "../ui/toaster"; -import { toast } from "../ui/use-toast"; import { ThemeProvider as NextThemesProvider } from "next-themes"; import { type ThemeProviderProps } from "next-themes/dist/types"; +import { getQueryClient } from "@/lib/utils"; export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children}; @@ -17,71 +17,6 @@ const penumbraTransport = createGrpcWebTransport({ baseUrl: "https://grpc.testnet.penumbra.zone", }); -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 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}`, - }); - } - }, - }), - }); -}; - -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; - } -}; - const Providers = ({ children } : { children: React.ReactNode }) => { // NOTE: there is a very explicit warning in the TanStack docs about using useState for handling QueryClient (de)hydration within the provider in the scenario where diff --git a/src/components/Transaction/getTransaction.ts b/src/components/Transaction/getTransaction.ts new file mode 100644 index 0000000..a02b47b --- /dev/null +++ b/src/components/Transaction/getTransaction.ts @@ -0,0 +1,14 @@ +import { TransactionData } from "@/lib/validators/search"; + +export default async function getTransaction({ endpoint, hash } : { endpoint: string, hash: string}) { + console.log(`Fetching: POST ${endpoint}?q=${hash}`); + const res = await fetch(`http://localhost:3000${endpoint}?q=${hash}`, { method: "GET" }); + const json = await res.json(); + console.log("Fetched Result:", json); + const result = TransactionData.safeParse(json); + if (result.success) { + return result.data; + } else { + throw new Error(result.error.message); + } +} diff --git a/src/components/Transaction/index.tsx b/src/components/Transaction/index.tsx index d28069c..97ea8ed 100644 --- a/src/components/Transaction/index.tsx +++ b/src/components/Transaction/index.tsx @@ -1,19 +1,26 @@ -import { type TransactionResultPayload } from "@/lib/validators/search"; +"use client"; import { JsonView, allExpanded, defaultStyles } from "react-json-view-lite"; import "react-json-view-lite/dist/index.css"; import Link from "next/link"; import { type FC } from "react"; import { TransactionView } from "../TransactionView"; -import { ibcRegistry } from "@/lib/protobuf"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import getTransaction from "./getTransaction"; interface TransactionProps { - txData: TransactionResultPayload + // txData: TransactionDataPayload + endpoint: string, + queryName: string, + hash: string, } -const Transaction : FC = ({ txData }) => { - const [txResult, penumbraTx] = txData; - // const abciEvents = txResult.events; +export const Transaction : FC = ({ endpoint, queryName, hash }) => { + const { data } = useSuspenseQuery({ + queryKey: [queryName, hash], + queryFn: () => getTransaction({ endpoint, hash }), + }); + const [ txResult, penumbraTx ] = data; return (
@@ -34,7 +41,7 @@ const Transaction : FC = ({ txData }) => {

Transaction Data

-            
+            
           
@@ -42,5 +49,3 @@ const Transaction : FC = ({ txData }) => {
); }; - -export default Transaction;