DEV Community

Cover image for Kita-Book: A react Book library app with context API
Agw-a
Agw-a

Posted on • Edited on

6 2

Kita-Book: A react Book library app with context API

This Project helps any one who wants to search for a book online and build an online library. It provides information details on the author, pages and description for each book. The main focus was the front-end interface of the application.

What I used in my project

Front-End

It is built with react JavaScript framework, react hooks for state management, react-router-dom or client-side routing, Axios to fetch Api and CSS to style the UI.

Back-End

Books in the library were populated with Draftbit books API.

App Features

  • Enables user manually browse through the book library
  • Enables the user to read more about the book when the cover image is clicked.
  • Enable users add books to a favorite list

Project Overview

I created a web library application. It lets you browse through a collection of books and add books to favorite, the favorite tab acts as the user's library.

Setting up the application

The Landing page and setting up the routes

Routing


          <Route path="/" element={<LandingPage />}/>
          {/* <Route path="/About" element={<About />}/> */}
          <Route path="/Library" element={<GeneralLibrary />}>

            <Route index element={<Booklist />}/>
            <Route path="Favorite-Books" element={<Favorites />}/>
          </Route>
          <Route path="Favorite-Books" element={<Favorites />}/>
          <Route path="/books/:id" element={<BookDetails />} />




Enter fullscreen mode Exit fullscreen mode
Defining context

The context defines add to favorite and remove books from favorite functions.

import React, { useContext, useState, createContext } from "react";

const AppContext = createContext(null);

export const useAppContext = () => {
  const context = useContext(AppContext);

  if (context === undefined) {
    throw new Error("AppContext must be within contextprovider");
  }
  return context;
};

const AppContextProvider = ({ children }) => {
  const [fav, setFavs] = useState([]);

  const addToFavorites = (book) => {
    const oldFavs = [...fav];
    const newFavs = oldFavs.concat(book);

    setFavs(newFavs);
  };

  const removeFromFavorites = (id) => {
    const oldFavs = [...fav];
    const newFavs = oldFavs.filter((book) => book.id !== id);
    setFavs(newFavs);
  };

  return (
    <AppContext.Provider value={{ fav, addToFavorites, removeFromFavorites }}>
      {children}
    </AppContext.Provider>
  );
};

export default AppContextProvider;
Enter fullscreen mode Exit fullscreen mode
The Library

Books in the library were populated with Draftbit books API.

import React, { useState, useEffect } from "react";
import { LIBRARY_URL } from "./LibraryApi/LibraryApi";
import axios from "axios";
import { useAppContext } from "../context/Context";
import { useNavigate } from "react-router-dom";
import "../styles/App.css";
import Loader from "./Loader";
import Pagnation from "./Pagnation";
import AddFavorite from "./interactions/add";
import RemoveFavorite from "./interactions/remove";

const Booklist = () => {
  const navigate = useNavigate();
  const [loading, setLoading] = useState(false);

  const [books, setBooks] = useState([]);

  const [postPerPage, setPostPerPage] = useState([12]);
  const [currentPage, setcurrentPage] = useState([1]);

  const { fav, addToFavorites, removeFromFavorites } = useAppContext();

  const checkFavoriteBooks = (id) => {
    const Checker = fav.some((book) => book.id === id);

    return Checker;
  };

  const lastPostIndex = currentPage * postPerPage;
  const firstpostIndex = lastPostIndex - postPerPage;

  const currentPost = books.slice(firstpostIndex, lastPostIndex);

  useEffect(() => {
    setLoading(true);
    axios
      .get(LIBRARY_URL)
      .then((response) => {
        setBooks(response.data);
        setLoading(false);
      })
      .catch((err) => console.log(err));
  }, []);

  return (
    <section>
      <div className="Book-list">
        {loading ? (
          <Loader />
        ) : (
          currentPost.map((book) => {
            return (
              <div key={book.id} className="book-cover">
                <div>
                  <img
                    className="individual-cover"
                    onClick={() => navigate(`/books/${book.id}`)}
                    src={book.image_url}
                    alt={`Cover of ${book.title}`}
                  />
                </div>

                <div className="display-book-details">
                  <div className="book-title">
                    <h4> {book.title}</h4>
                  </div>

                  <div className="author-display">
                    <span>{book.num_pages}</span> <span>|</span>{" "}
                    <span>{book.format}</span>
                  </div>

                  <div className="author-display">
                    {checkFavoriteBooks(book.id) ? (
                      <button onClick={() => removeFromFavorites(book.id)}>
                        {<RemoveFavorite />}
                      </button>
                    ) : (
                      <button onClick={() => addToFavorites(book)}>
                        {<AddFavorite />}
                      </button>
                    )}
                  </div>
                </div>
              </div>
            );
          })
        )}
      </div>

      <div className="paginate">
        <Pagnation
          totalPosts={books.length}
          postsPerPage={postPerPage}
          setCurrentPage={setcurrentPage}
          currentPage={currentPage}
        />
      </div>
    </section>
  );
};

export default Booklist;
Enter fullscreen mode Exit fullscreen mode
Defining the user library-Favorite books
import React from "react";
import { useAppContext } from "../context/Context";

import AddFavorite from "./interactions/add";
import RemoveFavorite from "./interactions/remove";

const FavoriteBooks = () => {
  const { fav, addToFavorites, removeFromFavorites } = useAppContext();

  const checkFavoriteBooks = (id) => {
    const Checker = fav.some((book) => book.id === id);

    return Checker;
  };
  return (
    <div className="fav-page">
      <span className="for-you-title">Your Library</span>

      <div className="fav-books-container">
        {fav.length > 0 ? (
          fav.map((book) => {
            return (
              <div key={book.id} className="fav-books">
                <div>
                  <img
                    className="fav-book-img"
                    src={book.image_url}
                    alt={`Cover of ${book.title}`}
                  />
                </div>

                <div>
                  <div className="fav-book-title">
                    <span> {book.title}</span>
                  </div>

                  <div className="fav-button">
                    {checkFavoriteBooks(book.id) ? (
                      <button onClick={() => removeFromFavorites(book.id)}>
                        {<RemoveFavorite />}
                      </button>
                    ) : (
                      <button onClick={() => addToFavorites(book)}>
                        {<AddFavorite />}
                      </button>
                    )}
                  </div>
                </div>
              </div>
            );
          })
        ) : (
          <div className="favorite-books-empty"></div>
        )}
      </div>
    </div>
  );
};

export default FavoriteBooks;
Enter fullscreen mode Exit fullscreen mode
Getting details to each book
import React from "react";
import "../styles/App.css";
import "../styles/smallScreens.css";
import { useParams, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import axios from "axios";
import { Book_Details_Url } from "./LibraryApi/LibraryApi";
import NavBar from "./NavBar";
import Footer from "./Footer";

const BookDetails = () => {
  const navigate = useNavigate();

  const goBack = () => {
    navigate("/Library");
  };

  const { id } = useParams();
  const [book, setBook] = useState({});

  useEffect(() => {
    axios
      .get(`${Book_Details_Url}/${id}`)
      .then((response) => {
        setBook(response.data);
      })
      .catch((err) => console.log(err));
  }, [id]);
  return (
    <>
      <NavBar />
      <main className="detail-background">
        <button className="detail-button" onClick={goBack}>
          {" "}
           Go back
        </button>

        <div className="detail-container">
          <div className="detail-card">
            <div className="detail-img">
              <img src={book?.image_url} alt={book?.title} />
            </div>
            <div className="detail-text">
              <p className="detail-book-titles">{book?.title}</p>
              <p className="detail-titles-author">Author | {book?.authors}</p>
              <div className="detail-description">
                <p className="description-text">{book?.description}</p>
                <p className="description-pages">Pages | {book?.num_pages} </p>

                <p className="description-genres">
                  Genres | {book?.genre_list}
                </p>
              </div>
              <div className="detail-quote">
                <blockquote>
                  <p>{book?.Quote1}</p>
                  <cite>{book?.authors}</cite>
                </blockquote>
              </div>
            </div>
          </div>
        </div>
      </main>

      <Footer />
    </>
  );
};

export default BookDetails;
Enter fullscreen mode Exit fullscreen mode
Some challenges Challenges I faced

During the planning phase of the project, I planned to add a search feature to the app which would have enabled users to search for a book based on the book title or author, this was meant to be done with the goodreads API. I however plan on working on the project further to include the search feature and more book ordering categories.

Find full code in here

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️