Skip to content

Commit

Permalink
removed local useToast in childcomponents and configured queryClient …
Browse files Browse the repository at this point in the history
…with queryCache to provide global error management for toast popups where relevant. removed remaining idiosyncratic and inconsistent stylings from booloean conditionals if statements
  • Loading branch information
ejmg committed Nov 1, 2023
1 parent 0b85d50 commit 45987a1
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 61 deletions.
2 changes: 1 addition & 1 deletion src/app/api/ht/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function GET(req: Request) {

return new Response(JSON.stringify(block));
} catch (error) {
if ( error instanceof z.ZodError ) {
if (error instanceof z.ZodError) {
return new Response("Invalid block height query.", { status: 422 });
}
return new Response("Could not find block by height.", { status: 404 });
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/tx/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function GET(req: Request) {

return new Response(JSON.stringify(tx));
} catch (error) {
if ( error instanceof z.ZodError ) {
if (error instanceof z.ZodError) {
return new Response("Invalid transaction query: Hash must be 64 hexadecimal characters with optional 0x prefix", { status: 422 });
}
return new Response("Could not find transaction result with provided hash.", { status: 404 });
Expand Down
22 changes: 7 additions & 15 deletions src/app/block/[ht]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import { useEffect, type FC } from "react";
import { type FC } from "react";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { BlockResult } from "@/lib/validators/search";
import { toast } from "@/components/ui/use-toast";
import BlockEvent from "@/components/BlockEvent";

interface PageProps {
Expand All @@ -20,27 +19,20 @@ const Page : FC<PageProps> = ({ params }) => {
queryFn: async () => {
const { data } = await axios.get(`/api/ht?q=${ht}`);
const result = BlockResult.safeParse(data);
if ( result.success ) {
if (result.success) {
return result.data;
} else {
throw new Error(result.error.message);
}
},
queryKey: ["htQuery", ht],
retry: false,
meta: {
errorMessage: "Failed to find block event with provided height. Please check height or try a different query.",
},
});

useEffect(() => {
if (isError) {
toast({
variant: "destructive",
title: "Error",
description: "Failed to find block event with provided height. Please check height or try a different query.",
});
}
}, [isError]);

if ( isError ) {
if (isError) {
return (
<div className="py-5 flex justify-center">
<h1 className="text-4xl font-semibold">No results found.</h1>
Expand All @@ -51,7 +43,7 @@ const Page : FC<PageProps> = ({ params }) => {
// TODO: Replace with data table component views once those are fleshed out.
return (
<div>
{ isFetched ? (
{isFetched ? (
<div>
{blockData ? (
<div className="flex flex-col justify-center w-full">
Expand Down
7 changes: 5 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ export default async function Home() {
queryFn: async () => {
const { data } = await axios.get(`/api/events?page=${1}`);
const result = TableEvents.safeParse(data);
if ( result.success ) {
if (result.success) {
return result.data;
} else {
// NOTE: another reason to move this hydration further down the component tree, ie create a completely generic data-table component and
// make independent BlockEventTable/TransactionEventTable server generated components that performs the hydration, is that
// it will allow for toaster errors without losing access to static server rendering.
// It's that or going back to react-hot-toast which provides a headless UI solution for this precise issue.
throw result.error;
throw new Error(result.error.message);
}
},
meta: {
errorMessage: "Failed to query data while trying to generate event table, please try reloading the page.",
},
});

return (
Expand Down
28 changes: 7 additions & 21 deletions src/app/tx/[hash]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import { useEffect, type FC } from "react";
import { type FC } from "react";
import { useQuery } from "@tanstack/react-query";
import { TransactionResult } from "@/lib/validators/search";
import axios from "axios";
import { useToast } from "@/components/ui/use-toast";
import TransactionEvent from "@/components/TransactionEvent";

interface PageProps {
Expand All @@ -16,38 +15,25 @@ 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<PageProps> = ({ params }) => {
const { hash } = params;
const { toast } = useToast();

const { data: txData , isFetched, isError } = useQuery({
queryFn: async () => {
const { data } = await axios.get(`/api/tx?q=${hash}`);
const result = TransactionResult.safeParse(data);
if ( result.success ) {
if (result.success) {
return result.data;
} else {
throw new Error(result.error.message);
}
},
queryKey: ["txQuery", hash],
retry: false,
meta: {
errorMessage: "Failed to find transaction event with provided hash. Please check hash or try a different query.",
},
});

// TODO: This is a hack of a compromise right now for the following reasons:
// 1. Ideally, want to descriminate the error message where relevant to the end user (input error vs server error, etc).
// 2. Doing this in queryFn is messy and doing it for every client useQuery is even messier
// 3. However, not doing error refinement in useQuery results in a re-render error as the page state is resolved.
// Until error refinement is nailed down, useEffect delays the error toast to avoid re-render runtime errors.
useEffect(() => {
if (isError) {
toast({
variant: "destructive",
title: "Error",
description: "Failed to find transaction event with provided hash. Please check hash or try a different query.",
});
}
}, [isError]);

if ( isError ) {
if (isError) {
return (
<div className="py-5 flex justify-center">
<h1 className="text-4xl font-semibold">No results found.</h1>
Expand All @@ -61,7 +47,7 @@ const Page : FC<PageProps> = ({ params }) => {
<div>
{isFetched ? (
<div>
{ txData ? (
{txData ? (
<div className="flex flex-col justify-center w-full">
<h1 className="text-3xl mx-auto py-5 font-semibold">Transaction Event Summary</h1>
<TransactionEvent txEvent={txData} />
Expand Down
22 changes: 6 additions & 16 deletions src/components/EventTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { columns } from "./columns";
import { TableEvents } from "@/lib/validators/table";
import axios from "axios";
import { useQuery } from "@tanstack/react-query";
import { useToast } from "../ui/use-toast";
import { useEffect } from "react";

// TODO: resolve these typings and that with zod and how to navigate between them.
// NOTE: is it possible to derive a tuple type that encodes the valid combinations of event attributes and their types?
Expand Down Expand Up @@ -37,33 +35,25 @@ export interface TransactionResult {
// TODO: Could try extracting out a minimal data table representation that can then be modified for different query types
// such as Blocks vs Block Events vs Transaction Results vs Transaction Events etc.
const EventTable = () => {
const { toast } = useToast();
const { data: eventData, isLoading, isError } = useQuery({
const { data: eventData, isLoading } = useQuery({
queryFn: async () => {
const { data } = await axios.get(`/api/events?page=${1}`);
const result = TableEvents.safeParse(data);
if ( result.success ) {
if (result.success) {
return result.data;
} else {
throw new Error(result.error.message);
}
},
queryKey: ["eventTableQuery"],
meta: {
errorMessage: "Failed to query data while trying to generate event table, please try reloading the page.",
},
});

useEffect(() => {
if (isError) {
toast({
variant: "destructive",
title: "Error",
description: "Failed to query data while trying to generate event table, please try reloading the page.",
});
}
}, [isError]);

return (
<div>
{ !isLoading ? (
{!isLoading ? (
<div>
{eventData ? (
<div className="container mx-auto py-10">
Expand Down
30 changes: 29 additions & 1 deletion src/components/Providers/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
"use client";

import React, { useState } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "../ui/toaster";
import { useToast } from "../ui/use-toast";

const Providers = ({ children } : { children: React.ReactNode }) => {
const { toast } = useToast();
const [ queryClient ] = useState(() => new QueryClient({
defaultOptions: {
queries: {
// Direct suggestion by tanstack, to prevent over-eager refetching from the client.
staleTime: 60 * 1000,
},
},
queryCache: new QueryCache({
onError: (error, query) => {
// 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}`,
});
}
},
}),
}));

return (
Expand Down
6 changes: 3 additions & 3 deletions src/components/Searchbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ const SearchBar : FC = () => {
}}
onKeyDown={(e) => {
// Aside: Now that this is just a single command input, maybe just convert this to a generic input box?
if ( e.key === "Enter" && input.length !== 0 ) {
if (e.key === "Enter" && input.length !== 0) {
const hashResult = HashResultValidator.safeParse(input);
const blockResult = BlockHeightValidator.safeParse(input);
if ( hashResult.success ) {
if (hashResult.success) {
searchCmd("tx");
} else if ( blockResult.success ) {
} else if (blockResult.success) {
searchCmd("block");
} else {
toast({
Expand Down
2 changes: 1 addition & 1 deletion src/lib/validators/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const HashResultValidator = z.union([
z.string().toUpperCase().length(64).regex(/^([A-F0-9]{64})$/, { message: "Hash must be 64 hexadecimal characters with optional 0x prefix." }),
]).transform((val) => {
// Trim 0X if our hash is prefixed, otherwise return as is.
if ( val.length === 66 ) {
if (val.length === 66) {
return val.slice(2);
}
return val;
Expand Down

0 comments on commit 45987a1

Please sign in to comment.