DEV Community

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

Posted on • Updated on

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

Top comments (0)