From 5ea924c5adce8c9508fd1ea8e79be3de501791ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Catarino?= Date: Fri, 2 May 2025 18:12:19 +0200 Subject: [PATCH] refactor: better data fetching (#100) --- web/src/App.tsx | 8 +++-- web/src/components/DataLoadingError.tsx | 30 +++++++++++++++++++ web/src/components/Loading.tsx | 12 +------- web/src/hooks/use-data.tsx | 40 +++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 web/src/components/DataLoadingError.tsx create mode 100644 web/src/hooks/use-data.tsx diff --git a/web/src/App.tsx b/web/src/App.tsx index 34e563f..07c15b2 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -4,11 +4,12 @@ import Statistic from "./components/Statistic"; import Image from "./components/Image"; import { LastChecked } from "./components/LastChecked"; import Loading from "./components/Loading"; -import { Data } from "./types"; import { theme } from "./theme"; import RefreshButton from "./components/RefreshButton"; import Search from "./components/Search"; import { Server } from "./components/Server"; +import { useData } from "./hooks/use-data"; +import DataLoadingError from "./components/DataLoadingError"; const SORT_ORDER = [ "monitored_images", @@ -22,10 +23,11 @@ const SORT_ORDER = [ ]; function App() { - const [data, setData] = useState(null); + const { data, isLoading, isError } = useData(); const [searchQuery, setSearchQuery] = useState(""); - if (!data) return ; + if (isLoading) return ; + if (isError || !data) return ; return (
{ + return ( +
+
+
+
+

+ Cup +

+ +
+
+
+ An error occurred, please try again. +
+
+
+
+
+ ); +}; + +export default DataLoadingError; diff --git a/web/src/components/Loading.tsx b/web/src/components/Loading.tsx index 400f2ee..f4850c8 100644 --- a/web/src/components/Loading.tsx +++ b/web/src/components/Loading.tsx @@ -1,18 +1,8 @@ -import { Data } from "../types"; import Logo from "./Logo"; import { theme } from "../theme"; import { LoaderCircle } from "lucide-react"; -export default function Loading({ onLoad }: { onLoad: (data: Data) => void }) { - fetch( - process.env.NODE_ENV === "production" - ? "./api/v3/json" - : `http://${window.location.hostname}:8000/api/v3/json`, - ).then((response) => - response.json().then((data) => { - onLoad(data as Data); - }), - ); +export default function Loading() { return (
{ + const [isLoading, setIsLoading] = useState(false); + const [isError, setIsError] = useState(false); + const [data, setData] = useState(null); + + useEffect(() => { + if (isLoading || isError || !!data) return; + setIsLoading(true); + setIsError(false); + setData(null); + fetch( + process.env.NODE_ENV === "production" + ? "./api/v3/json" + : `http://${window.location.hostname}:8000/api/v3/json`, + ) + .then((response) => { + if (response.ok) return response.json(); + throw new Error("Failed to fetch data"); + }) + .then((data) => { + setData(data as Data); + }) + .catch((error: unknown) => { + setIsError(true); + console.error(error); + }) + .finally(() => { + setIsLoading(false); + }); + }, [data, isError, isLoading]); + + return { + data, + isLoading, + isError, + }; +};