diff --git a/src/App.tsx b/src/App.tsx index d4a7658..f8f8c3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,11 +3,21 @@ import { Header } from "./components/Header/Header.tsx"; import { Footer } from "./components/Footer/Footer.tsx"; import { BookGrid } from "./components/BookGrid/BookGrid.tsx"; import { BookDetails } from "./components/BookDetails/BookDetails.tsx"; +import { useBooks } from "./api/useBooks.ts"; +import { useDebounce } from "./components/BookGrid/useDebounce.ts"; import type { Book } from "./api/types.ts"; function App() { const [selectedBook, setSelectedBook] = useState(null); const [page, setPage] = useState(1); + // Search state and the fetched books live here instead of in BookGrid, so + // they survive switching to BookDetails and back. + const [searchQuery, setSearchQuery] = useState(""); + + const debouncedSearch = useDebounce(searchQuery, 500); + const query = debouncedSearch.trim() || "subject:fantasy"; + + const booksQuery = useBooks({ query, page }); return ( <> @@ -16,10 +26,15 @@ function App() { {selectedBook ? ( setSelectedBook(null)} /> ) : ( void; onGoBack: () => void; } -function BookDetails({ book, onGoBack }: BookDetailsProps) { +function BookDetails({ + book, + books, + onBookSelect, + onGoBack, +}: BookDetailsProps) { const { data: description, isPending, isError } = useBookDescription(book.id); const descriptionText = isPending ? "Loading description..." : isError ? "Error loading description" : (description ?? "No description available"); + + // index is -1 if the list has changed since the book was opened; then both + // browse buttons are disabled. + const index = books.findIndex((b) => b.id === book.id); + const previousBook = index > 0 ? books[index - 1] : undefined; + const nextBook = index >= 0 ? books[index + 1] : undefined; + return (
@@ -26,6 +41,30 @@ function BookDetails({ book, onGoBack }: BookDetailsProps) { > Go back +
diff --git a/src/components/BookGrid/BookGrid.tsx b/src/components/BookGrid/BookGrid.tsx index 26c8444..81c3b71 100644 --- a/src/components/BookGrid/BookGrid.tsx +++ b/src/components/BookGrid/BookGrid.tsx @@ -1,32 +1,33 @@ -import { useState } from "react"; import { BookCard } from "../BookCard/BookCard"; import { SearchBar } from "../SearchBar/SearchBar"; import styles from "./BookGrid.module.css"; -import { useBooks } from "../../api/useBooks"; -import { useDebounce } from "./useDebounce"; +import type { useBooks } from "../../api/useBooks"; import type { Book } from "../../api/types"; import { Pagination } from "../Pagination/Pagination"; import { SEARCH_LIMIT } from "../../api/openLibrary"; interface BookGridProps { + // The result of useBooks, owned by App so it outlives this component. + booksQuery: ReturnType; + searchQuery: string; + onSearchChange: (query: string) => void; page: number; onBookSelect: (book: Book) => void; onPageChange: (page: number) => void; } -function BookGrid({ page, onBookSelect, onPageChange }: BookGridProps) { - const [searchQuery, setSearchQuery] = useState(""); - - const debouncedSearch = useDebounce(searchQuery, 500); - const query = debouncedSearch.trim() || "subject:fantasy"; - - const { data, isPending, isError, error } = useBooks({ - query, - page, - }); +function BookGrid({ + booksQuery, + searchQuery, + onSearchChange, + page, + onBookSelect, + onPageChange, +}: BookGridProps) { + const { data, isPending, isError, error } = booksQuery; const searchBar = ( - + ); if (isPending) {