Skip to content

Commit

Permalink
feat: Enhance book fetching logic
Browse files Browse the repository at this point in the history
Update the book fetching logic to include the isFavorited status directly from the server.

This eliminates the need for a separate request to fetch user favorites.
  • Loading branch information
amalv committed Jan 4, 2024
1 parent 1373df9 commit d561d59
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 67 deletions.
12 changes: 2 additions & 10 deletions src/components/LibraryPage/components/Books/Books.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,15 @@ export interface BooksProps {

export const Books = ({ search, limit }: BooksProps) => {
const {
isBookFavorited,
isErrorSnackbarOpen,
loading,
favoritesLoading,
error,
data,
loader,
handleCloseSnackbar,
} = useBooks({ search, limit });

const books =
data?.books?.books.map((book) => ({
...book,
isFavorited: isBookFavorited(book.id),
})) ?? [];

if (loading || favoritesLoading) {
if (loading) {
return <LoadingView />;
}

Expand All @@ -38,7 +30,7 @@ export const Books = ({ search, limit }: BooksProps) => {
handleCloseSnackbar={handleCloseSnackbar}
/>
) : (
<BooksView books={books} />
<BooksView books={data?.books.books || []} />
)}
<div ref={loader} />
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ test("renders book information correctly", async () => {
id: "1",
title: "Test Book",
author: "Test Author",
isFavorited: false,
publicationDate: "2000-01-01",
image: "https://example.com/image.jpg",
rating: 80,
Expand All @@ -16,7 +17,7 @@ test("renders book information correctly", async () => {

render(
<MockedProvider>
<BookCard book={mockBook} isFavorited={false} />
<BookCard book={mockBook} />
</MockedProvider>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import { Book } from "../../../../../../data/books";

interface BookCardProps {
book: Book;
isFavorited: boolean;
}

export const BookCard: React.FC<BookCardProps> = ({ book, isFavorited }) => (
export const BookCard: React.FC<BookCardProps> = ({ book }) => (
<Grid item xs={6} sm={4} md={4} lg={3} xl={2} key={book.title}>
<Box position="relative">
<FavoriteButton bookId={book.id} isFavorited={isFavorited} />
<FavoriteButton bookId={book.id} isFavorited={book.isFavorited} />
</Box>
<CardContent book={book} />
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const FavoriteButton: React.FC<FavoriteButtonProps> = ({
bookId,
isFavorited: initialIsFavorited,
}) => {
const { isFavorited, handleFavoriteClick } = useFavoriteButton(
const { handleFavoriteClick, isFavorited } = useFavoriteButton(
bookId,
initialIsFavorited
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useCallback } from "react";
import { useEffect, useState, useCallback } from "react";
import { useAuth0 } from "@auth0/auth0-react";
import { useFavorite } from "../useFavorite";
import { FetchResult } from "@apollo/client";
Expand All @@ -25,6 +25,10 @@ export const useFavoriteButton = (
const { addFavorite, removeFavorite } = useFavorite();
const [isFavorited, setIsFavorited] = useState(initialIsFavorited);

useEffect(() => {
setIsFavorited(initialIsFavorited);
}, [initialIsFavorited]);

const handleFavoriteClick = useCallback(() => {
if (!user) {
loginWithRedirect();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { BookCard, Message } from "../";
import { Book } from "../../../../../../data/books";
import { Book } from "@/data/books";

interface BookWithFavoriteStatus extends Book {
isFavorited: boolean;
}

export const BooksView = ({ books }: { books: BookWithFavoriteStatus[] }) =>
export const BooksView = ({ books }: { books: Book[] }) =>
books.length > 0 ? (
books.map((book: BookWithFavoriteStatus) => (
<BookCard key={book.id} book={book} isFavorited={book.isFavorited} />
))
books.map((book: Book) => <BookCard key={book.id} book={book} />)
) : (
<Message text="No books available" />
);
54 changes: 12 additions & 42 deletions src/components/LibraryPage/components/Books/hooks/useBooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { useEffect, useRef, useState, useCallback } from "react";
import { useEffect, useRef, useState } from "react";
import { useAuth } from "@/contexts";
import { useAuth0 } from "@auth0/auth0-react";
import { ApolloError, useQuery } from "@apollo/client";
import { BOOKS_QUERY, BooksData, BooksVars } from "@/data/books";
import { FavoritesData, GET_FAVORITES_QUERY } from "@/data/favorites";
import { useIntersectionObserver } from "./useIntersectionObserver";
import { getCloseSnackbarHandler, getUpdateQuery } from "./utils";
import { useFetchMoreBooks } from "./useFetchMoreBooks";
import { useAuth } from "@/contexts";

const PAGE_SIZE = 50;

Expand All @@ -32,16 +31,15 @@ const useHandleError = (error: ApolloError | undefined) => {
const useHandleLastPageReached = (
search: string,
limit: number,
isAuthenticated: boolean,
favoritesLoading: boolean
isAuthenticated: boolean
) => {
const lastPageReachedRef = useRef(false);
const [lastPageReached, setLastPageReached] = useState(false);

useEffect(() => {
setLastPageReached(false);
lastPageReachedRef.current = false;
}, [search, limit, isAuthenticated, favoritesLoading]);
}, [search, limit, isAuthenticated]);

useEffect(() => {
lastPageReachedRef.current = lastPageReached;
Expand All @@ -55,37 +53,23 @@ export const useBooks = ({ search, limit }: UseBooksProps) => {
const { token } = useAuth();
const tokenRef = useRef(token);

const { loading, error, data, fetchMore } = useQuery<BooksData, BooksVars>(
BOOKS_QUERY,
{
variables: { title: search, author: search, limit, cursor: "0" },
}
);

// Fetch user favorites
const {
loading: favoritesLoading,
error: favoritesError,
data: favoritesData,
refetch: refetchFavorites,
} = useQuery<FavoritesData>(GET_FAVORITES_QUERY, {
skip: !token,
const { loading, error, data, fetchMore, refetch } = useQuery<
BooksData,
BooksVars
>(BOOKS_QUERY, {
variables: { title: search, author: search, limit, cursor: "0" },
});

useEffect(() => {
if (token && token !== tokenRef.current) {
refetchFavorites();
refetch();
}
tokenRef.current = token;
}, [refetchFavorites, token]);
}, [token, refetch]);

const { isErrorSnackbarOpen, handleCloseSnackbar } = useHandleError(error);
const {
isErrorSnackbarOpen: isFavoritesErrorSnackbarOpen,
handleCloseSnackbar: handleCloseFavoritesSnackbar,
} = useHandleError(favoritesError);
const { lastPageReached, setLastPageReached, lastPageReachedRef } =
useHandleLastPageReached(search, limit, isAuthenticated, favoritesLoading);
useHandleLastPageReached(search, limit, isAuthenticated);

const updateQuery = getUpdateQuery(setLastPageReached);
const loader = useRef(null);
Expand All @@ -106,26 +90,12 @@ export const useBooks = ({ search, limit }: UseBooksProps) => {

useIntersectionObserver(loader, handleFetchMore, loading, lastPageReachedRef);

const isBookFavorited = useCallback(
(bookId: string) => {
const userFavorites = favoritesData?.user?.favorites || [];
return userFavorites.some((favorite) => favorite.book.id === bookId);
},
[favoritesData]
);

return {
isBookFavorited,
isErrorSnackbarOpen,
isFavoritesErrorSnackbarOpen,
handleCloseSnackbar,
handleCloseFavoritesSnackbar,
loading,
favoritesLoading,
error,
favoritesError,
data,
favoritesData,
loader,
};
};
2 changes: 2 additions & 0 deletions src/data/books.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type Book = {
id: string;
title: string;
author: string;
isFavorited: boolean;
publicationDate: string;
image: string;
rating: number;
Expand Down Expand Up @@ -37,6 +38,7 @@ export const BOOKS_QUERY = gql`
id
title
author
isFavorited
publicationDate
image
rating
Expand Down

0 comments on commit d561d59

Please sign in to comment.