Use suspense / error boundary

This commit is contained in:
Adrian Rumpold
2025-05-22 08:13:43 +02:00
parent 7ccaa531e1
commit 0335e0cedb
6 changed files with 72 additions and 25 deletions

View File

@@ -6,16 +6,13 @@ import StringFilter from "./components/StringFilter/StringFilter";
import useGlossary from "./hooks/useGlossary";
function App() {
const { glossary, citations, isLoading, isError } = useGlossary();
const { glossary, citations } = useGlossary();
const [alphabeticalFilter, setAlphabeticalFilter] = useState("");
const [stringFilter, setStringFilter] = useState("");
const [selectedCitation, setSelectedCitation] = useState<string | null>(null);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error while fetching</div>;
const entries = glossary!
const entries = glossary
.filter((entry) => {
if (!stringFilter) {
return true;
@@ -40,7 +37,7 @@ function App() {
{selectedCitation && (
<CitationEntry
citation={
citations!.find((citation) => citation.key === selectedCitation)!
citations.find((citation) => citation.key === selectedCitation)!
}
onClose={() => setSelectedCitation(null)}
/>

View File

@@ -0,0 +1,9 @@
.error-boundary {
color: red;
font-size: 1.2rem;
text-align: center;
pre {
color: var(--color-text);
}
}

View File

@@ -0,0 +1,43 @@
import type { ReactNode } from "react";
import React, { Component } from "react";
import "./ErrorBoundary.css";
interface ErrorBoundaryProps {
children: ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
componentDidCatch(_: Error, __: React.ErrorInfo) {
// You can log the error to an error reporting service here
}
render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h2>Something went wrong.</h2>
<pre>{this.state.error?.message}</pre>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;

View File

@@ -1,13 +1,13 @@
import { useQuery } from "@tanstack/react-query";
import { useSuspenseQuery } from "@tanstack/react-query";
import { fetchCitations, fetchDefinitions } from "../lib/nist-api";
const useGlossary = () => {
const definitionsQuery = useQuery({
const definitionsQuery = useSuspenseQuery({
queryKey: ["definitions"],
queryFn: fetchDefinitions,
staleTime: 1000 * 60 * 60, // 1 hour
});
const citationsQuery = useQuery({
const citationsQuery = useSuspenseQuery({
queryKey: ["citations"],
queryFn: fetchCitations,
staleTime: 1000 * 60 * 60, // 1 hour
@@ -16,8 +16,6 @@ const useGlossary = () => {
return {
glossary: definitionsQuery.data,
citations: citationsQuery.data,
isLoading: definitionsQuery.isLoading || citationsQuery.isLoading,
isError: definitionsQuery.isError || citationsQuery.isError,
};
};

View File

@@ -38,6 +38,7 @@ const citationGid = "2053825396";
const makeClient = () => {
const client = new Axios({
baseURL: baseUrl,
timeout: 2500,
});
return client;
};
@@ -62,18 +63,6 @@ export const fetchDefinitions = async () => {
return parseDefinitions(resp.data);
};
export const parseGlossary = (
glossaryData: string,
citationData: string
): Glossary => {
const glossary: Glossary = {
definitions: parseDefinitions(glossaryData),
citations: parseCitations(citationData),
};
return glossary;
};
const parseDefinitions = (glossaryData: string): GlossaryTerm[] => {
const terms: GlossaryTerm[] = [];
const parsed = Papa.parse<string[]>(glossaryData, {

View File

@@ -1,15 +1,26 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { StrictMode } from "react";
import { StrictMode, Suspense } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import ErrorBoundary from "./components/ErrorBoundary/ErrorBoundary";
import "./index.css";
const queryClient = new QueryClient();
createRoot(document.getElementById("root")!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<ErrorBoundary>
<Suspense
fallback={
<div style={{ textAlign: "center", marginTop: "2rem" }}>
Loading...
</div>
}
>
<App />
</Suspense>
</ErrorBoundary>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StrictMode>