From 6c6b959397327f188a24a23286cd983209025c2e Mon Sep 17 00:00:00 2001 From: mina Date: Wed, 16 Sep 2026 14:35:40 +0200 Subject: [PATCH 1/5] refactor: move main layout to App --- src/App.tsx | 8 +++++++- src/main.tsx | 2 -- src/pages/CharactersPage.tsx | 4 ++-- src/pages/HomePage.tsx | 6 +----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2812750..249cb9a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,14 @@ import "./App.css"; import HomePage from "./pages/HomePage"; +import CharactersPage from "./pages/CharactersPage"; function App() { - return ; + return ( +
+ + +
+ ); } export default App; diff --git a/src/main.tsx b/src/main.tsx index 31358ab..eff7ccc 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,11 +2,9 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; import App from "./App.tsx"; -import CharactersPage from "./pages/CharactersPage.tsx"; createRoot(document.getElementById("root")!).render( - , ); diff --git a/src/pages/CharactersPage.tsx b/src/pages/CharactersPage.tsx index ac82c9e..0cccda5 100644 --- a/src/pages/CharactersPage.tsx +++ b/src/pages/CharactersPage.tsx @@ -150,7 +150,7 @@ export default function CharactersPage() { } return ( -
+
handleToggleFavorite(selectedCharacter.id)} /> )} -
+ ); } diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index aafc02e..a51e7d2 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,11 +1,7 @@ import Hero from "../components/Hero"; function HomePage() { - return ( -
- -
- ); + return ; } export default HomePage; From ee2c901f54815a562350bdc863464cc7e0b6dd00 Mon Sep 17 00:00:00 2001 From: mina Date: Wed, 16 Sep 2026 14:40:48 +0200 Subject: [PATCH 2/5] feat: fetch characters with TanStack Query --- src/main.tsx | 7 ++++++- src/pages/CharactersPage.tsx | 34 +++++++++++++--------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index eff7ccc..b273eb5 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,15 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import "./index.css"; import App from "./App.tsx"; +const queryClient = new QueryClient() + createRoot(document.getElementById("root")!).render( - + + + , ); diff --git a/src/pages/CharactersPage.tsx b/src/pages/CharactersPage.tsx index 0cccda5..b0b5c7b 100644 --- a/src/pages/CharactersPage.tsx +++ b/src/pages/CharactersPage.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import CharacterCardSmall from "../components/CharacterCardSmall"; import CharacterDetailCard from "../components/CharacterDetailCard"; import SearchIcon from "../assets/search.svg"; +import { useQuery } from '@tanstack/react-query'; import type { Character } from "../types/Character"; @@ -15,8 +16,19 @@ const filters: { title: string; key: FilterCategory; options: string[] }[] = [ { title: "Status", key: "status", options: ["Alive", "Dead", "Unknown"] }, ]; +async function fetchCharacters(): Promise { + const response = await fetch("https://rickandmortyapi.com/api/character"); + + if (!response.ok) { + throw new Error(`Could not fetch characters: ${response.status}`); + } + + const data = await response.json(); + return data.results; +} + export default function CharactersPage() { - const [characters, setCharacters] = useState([]); + const {data: characters = []} = useQuery({queryKey: ['characters'], queryFn: fetchCharacters}); const [selectedCharacter, setSelectedCharacter] = useState( null, ); @@ -59,26 +71,6 @@ export default function CharactersPage() { ); }, [favoriteCharacterIds]); - useEffect(() => { - async function fetchCharacters() { - try { - const response = await fetch( - "https://rickandmortyapi.com/api/character", - ); - - if (!response.ok) { - throw new Error(`Could not fetch characters: ${response.status}`); - } - - const data = await response.json(); - setCharacters(data.results); - } catch (error) { - console.error(error); - } - } - fetchCharacters(); - }, []); - const filteredCharacters = characters.filter((character) => { const matchesSearch = character.name .toLowerCase() From f19e8fa738320615151b322737abfa5ffb286980 Mon Sep 17 00:00:00 2001 From: mina Date: Wed, 16 Sep 2026 14:43:17 +0200 Subject: [PATCH 3/5] feat: fetch episode data with TanStack Query --- src/components/CharacterDetailCard.tsx | 48 ++++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/CharacterDetailCard.tsx b/src/components/CharacterDetailCard.tsx index 9fb735a..c00c649 100644 --- a/src/components/CharacterDetailCard.tsx +++ b/src/components/CharacterDetailCard.tsx @@ -1,5 +1,5 @@ import "./CharacterDetailCard.css"; -import { useEffect, useState } from "react"; +import { useQuery } from '@tanstack/react-query' import type { Character } from "../types/Character"; import DetailCardShape from "../assets/detail-card-shape.svg"; @@ -23,6 +23,25 @@ type Episode = { episode: string; }; +// AI was used to create the fetchEpisodes function +async function fetchEpisodes(character: Character): Promise { + const episodeIds = character.episode + .map((episodeUrl) => episodeUrl.split('/').pop()) + .join(','); + + const response = await fetch( + `https://rickandmortyapi.com/api/episode/${episodeIds}` + ); + + if (!response.ok) { + throw new Error(`Could not fetch episodes: ${response.status}`); + } + + const data = await response.json(); + + return Array.isArray(data) ? data : [data]; +} + export default function CharacterDetailCard({ character, onClose, @@ -31,28 +50,11 @@ export default function CharacterDetailCard({ isFavorite, onToggleFavorite, }: CharacterDetailCardProps) { - const [episodes, setEpisodes] = useState([]); - - // AI was used to create this useEffect function. - useEffect(() => { - async function fetchEpisodes() { - try { - const episodeData = await Promise.all( - character.episode.map(async (episodeUrl) => { - const response = await fetch(episodeUrl); - if (!response.ok) { - throw new Error(`Could not fetch episode: ${response.status}`); - } - return response.json(); - }), - ); - setEpisodes(episodeData); - } catch (error) { - console.error(error); - } - } - fetchEpisodes(); - }, [character]); + + const { data: episodes = [] } = useQuery({ + queryKey: ['episodes', character.id], + queryFn: () => fetchEpisodes(character) + }); return (
From 0122a477ac02963d9546dd200737698514713e3a Mon Sep 17 00:00:00 2001 From: mina Date: Wed, 16 Sep 2026 14:44:23 +0200 Subject: [PATCH 4/5] chore: add sentence about AI use --- src/pages/CharactersPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/CharactersPage.tsx b/src/pages/CharactersPage.tsx index b0b5c7b..fc16ddd 100644 --- a/src/pages/CharactersPage.tsx +++ b/src/pages/CharactersPage.tsx @@ -16,6 +16,7 @@ const filters: { title: string; key: FilterCategory; options: string[] }[] = [ { title: "Status", key: "status", options: ["Alive", "Dead", "Unknown"] }, ]; +// AI was used to create the fetchCharacters function async function fetchCharacters(): Promise { const response = await fetch("https://rickandmortyapi.com/api/character"); From 5ac7558a19818822d2ddd80975b15767f278e35e Mon Sep 17 00:00:00 2001 From: mina Date: Wed, 16 Sep 2026 14:49:27 +0200 Subject: [PATCH 5/5] chore: add TanStack Query dependency --- package-lock.json | 27 +++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 28 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0a4af52..5c6bcfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "project-1", "version": "0.0.0", "dependencies": { + "@tanstack/react-query": "^5.103.0", "react": "^19.2.8", "react-dom": "^19.2.8" }, @@ -1068,6 +1069,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@tanstack/query-core": { + "version": "5.103.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.103.0.tgz", + "integrity": "sha512-PfafnHQHEu7mZDsSalxSW/EBxPgF77k3atIJeQbqbb1Z7DmK2mH6YLJyqrvbbDk5M6Ua/egs7LLEs04a8robxA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.103.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.103.0.tgz", + "integrity": "sha512-Kn/cvTrNwFYpS/q1MYghMsVTSinmAKV1U2AnM3/x07PiczvC+nMoySqNpQK1jgDzVCOIHk5LlF2FhTlJKQmPAQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.103.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.2", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz", diff --git a/package.json b/package.json index 74ba36f..1e069d2 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@tanstack/react-query": "^5.103.0", "react": "^19.2.8", "react-dom": "^19.2.8" },