Skip to content

Commit

Permalink
feat(game-details): tanstack query instead of custom hook for data re…
Browse files Browse the repository at this point in the history
…trieval
  • Loading branch information
barrenechea committed Dec 27, 2024
1 parent 34b4b62 commit e00855b
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 84 deletions.
57 changes: 57 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.6",
"@tanstack/react-query": "^5.62.11",
"@tanstack/react-router": "^1.92.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand All @@ -37,6 +38,7 @@
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@tanstack/react-query-devtools": "^5.62.11",
"@tanstack/router-devtools": "^1.92.6",
"@tanstack/router-plugin": "^1.91.1",
"@types/node": "^22.10.2",
Expand Down
91 changes: 13 additions & 78 deletions src/hooks/use-game-data.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,17 @@
import { useCallback, useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";

interface GameData {
commonTitle: string;
cover: string | null;
description: string;
developer: string;
discs: number;
genre: string;
id: string | string[];
languages: string[];
officialTitle: string;
publisher: string;
region: string;
releaseDate: string;
title: string;
}
import { fetchGameData, gameDataKeys } from "@/lib/query";

export function useGameData(platform: string, region: string, gameId: string) {
const [gameData, setGameData] = useState<GameData | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const fetchGameData = useCallback(async () => {
if (!platform || !region || !gameId) {
setGameData(null);
setError(null);
return;
}

setIsLoading(true);
setError(null);

try {
const apiRegion = mapRegionToApi(region);
const response = await fetch(
`https://psxdata.barrenechea.cl/${apiRegion}/${gameId}.json`
);

if (!response.ok) {
throw new Error("Failed to fetch game data");
}

const data = (await response.json()) as GameData;
setGameData({
...data,
cover: data.cover
? `https://psxdata.barrenechea.cl/${apiRegion}/covers/${gameId}.${data.cover.split(".").pop()}`
: null,
});
} catch (err) {
setError((err as Error).message);
setGameData(null);
} finally {
setIsLoading(false);
}
}, [platform, region, gameId]);

useEffect(() => {
void fetchGameData();
}, [fetchGameData]);

return { gameData, isLoading, error };
}

function mapRegionToApi(region: string): string {
switch (region.toLowerCase()) {
case "america":
case "usa":
case "ntsc-u":
return "America";
case "europe":
case "pal":
case "ntsc-pal":
return "Europe";
case "japan":
case "ntsc-j":
return "Japan";
default:
return "America"; // Default to America if unknown
}
const query = useQuery({
queryKey: gameDataKeys.details(region, gameId),
queryFn: () => fetchGameData(region, gameId),
enabled: Boolean(platform && region && gameId),
});

return {
gameData: query.data ?? null,
isLoading: query.isLoading,
error: query.error ? query.error.message : null,
};
}
73 changes: 73 additions & 0 deletions src/lib/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // Data is fresh for 5 minutes
gcTime: 1000 * 60 * 60, // Unused data is garbage collected after 1 hour
retry: 1,
},
},
});

export const gameDataKeys = {
all: ["game-data"] as const,
details: (region: string, gameId: string) =>
[...gameDataKeys.all, "details", region, gameId] as const,
};

export interface GameData {
commonTitle: string;
cover: string | null;
description: string;
developer: string;
discs: number;
genre: string;
id: string | string[];
languages: string[];
officialTitle: string;
publisher: string;
region: string;
releaseDate: string;
title: string;
}

function mapRegionToApi(region: string): string {
switch (region.toLowerCase()) {
case "america":
case "usa":
case "ntsc-u":
return "America";
case "europe":
case "pal":
case "ntsc-pal":
return "Europe";
case "japan":
case "ntsc-j":
return "Japan";
default:
return "America";
}
}

export async function fetchGameData(
region: string,
gameId: string
): Promise<GameData> {
const apiRegion = mapRegionToApi(region);
const response = await fetch(
`https://psxdata.barrenechea.cl/${apiRegion}/${gameId}.json`
);

if (!response.ok) {
throw new Error("Failed to fetch game data");
}

const data = (await response.json()) as GameData;
return {
...data,
cover: data.cover
? `https://psxdata.barrenechea.cl/${apiRegion}/covers/${gameId}.${data.cover.split(".").pop()}`
: null,
};
}
14 changes: 9 additions & 5 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import "./index.css";

import { QueryClientProvider } from "@tanstack/react-query";
import { createRouter, RouterProvider } from "@tanstack/react-router";
import { ThemeProvider } from "next-themes";
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";

import { LoadingDialogProvider } from "@/contexts/loading-dialog-context";
import { queryClient } from "@/lib/query";

// Import the generated route tree
import { routeTree } from "./routeTree.gen";
Expand All @@ -26,11 +28,13 @@ if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<ThemeProvider attribute="class">
<LoadingDialogProvider>
<RouterProvider router={router} />
</LoadingDialogProvider>
</ThemeProvider>
<QueryClientProvider client={queryClient}>
<ThemeProvider attribute="class">
<LoadingDialogProvider>
<RouterProvider router={router} />
</LoadingDialogProvider>
</ThemeProvider>
</QueryClientProvider>
</StrictMode>
);
}
12 changes: 11 additions & 1 deletion src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import { lazy, Suspense } from "react";

import { Sidebar } from "@/components/sidebar";

const ReactQueryDevtools = import.meta.env.PROD
? () => null // Render nothing in production
: lazy(() =>
// Lazy load in development
import("@tanstack/react-query-devtools").then((res) => ({
default: res.ReactQueryDevtools,
}))
);

const TanStackRouterDevtools = import.meta.env.PROD
? () => null // Render nothing in production
: lazy(() =>
Expand All @@ -25,7 +34,8 @@ export const Route = createRootRoute({
</div>
</div>
<Suspense>
<TanStackRouterDevtools position="bottom-right" />
<ReactQueryDevtools />
<TanStackRouterDevtools />
</Suspense>
</>
),
Expand Down

0 comments on commit e00855b

Please sign in to comment.