Part 3: How to Create a Book App with React.js - A Step-by-Step Tutorial

Part 3: How to Create a Book App with React.js - A Step-by-Step Tutorial

·

8 min read

The features I added in this concluding section are intended to enhance the project's value; they are not mandatory. You can include other features to further improve the look of your project.

In the previous tutorial, I provided a comprehensive guide on setting up the project, retrieving data from an API, dynamically displaying it on the webpage, and implementing dynamic search functionality.

To refresh your memory, I will include links to the previous articles.

Creating a quote page (optional)

Before incorporating the new feature into your project, ensure that the quote is linked in the Navbar.jsx component, allowing for easy navigation to the quote page.


import React from 'react';

const Navbar = () => {
  return (
    <div>
      <div>
        <h1>CRđź“šđź’–</h1>
        <nav>
          <ul>
            <li>
              <a href='/'>
                Home
              </a>
            </li>
            <li>
              <a href='/quote'>
                Quotesđź“”
              </a>
            </li>
          </ul>
        </nav>
      </div>
    </div>
  );
};

export default Navbar;

To learn how this was accomplished, please refer to the following link: ijaycent.hashnode.dev/part-1-how-to-create-..

Here are the simple steps to follow in order to get started with creating the quotes:

  • To begin, go to the file where you wish to create the array.

  • Create a variable using the const keyword, followed by the name you want to give your array. For example: const quotes = [ ];

  • Inside the square brackets, add the quotes you want to display, separated by commas.

example

// an array of quotes
    const quotes = [
    {
        title: "The more that you read, the more things you will know. The more that you learn, the more places you'll go. - Dr. Seuss",
        id: 1,
    },
    {
        title: "A reader lives a thousand lives before he dies, said Jojen. The man who never reads lives only one. - George R.R. Martin",
        id: 2,
    },
    {
        title: "The reading of all good books is like a conversation with the finest minds of past centuries.- Rene Descartes",
        id: 3,
    },
    {
        title: "Books are the quietest and most constant of friends; they are the most accessible and wisest of counselors, and the most patient of teachers. - Charles William Eliot",
        id: 4,
    },
    {
        title: "Reading is to the mind what exercise is to the body. - Joseph Addison",
        id: 5,
    },
    {
        title: "Reading is escape, and the opposite of escape; it's a way to make contact with reality after a day of making things up, and it's a way of making contact with someone else's imagination after a day that's all too real. - Nora Ephron",
        id: 6,
    },
    {
        title: "The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid. - Jane Austen",
        id: 7,
    },
    {
        title: "One glance at a book and you hear the voice of another person, perhaps someone dead for 1,000 years. To read is to voyage through time.- Carl Sagan",
        id: 8,
    },
    {
        title: "Reading gives us someplace to go when we have to stay where we are. - Mason Cooley",
        id: 9,
    },
    {
        title: "I have always imagined that Paradise will be a kind of library.- Jorge Luis Borges",
        id: 10,
    },
];
  • Import useState() from the "react" library at the top of your file.

  • Next, create a state variable to store the selected quote and initialize it to null using the useState() method. It's important to note that setting it to null represents the intentional absence of any object value.

In JavaScript, null is considered one of the primitive values and is treated as falsy for boolean operations.

Here is an example:

import { useState } from 'react'
const [selectedQuote, setSelectedQuote] = useState(null)

To display the selected quote on the webpage

  • Dynamically return the quotes that are stored in theuseState() and use a conditional operator to conditionally render them. This ensures that the quote is only displayed when it has been selected.
{selectedQuote && <h2>{selectedQuote.title}</h2>}

To make the button clickable on your webpage:

  • Create a button element in the return statement of your component.

  • Import the refresher icon from the react-icons library. Before you can use it, you need to install it in your application. You can install it using npm or yarn

// using npm
npm install react-icons

// using yarn
yarn add react-icons
import { BiRefresh } from 'react-icons/bi';
return (
 <div>
    <button type='button' onClick={handleClick}>
      Click to generate a quote{' '}
      <span>
        <BiRefresh />
      </span>
    </button>
  </div>
)

To display a random quote when a user clicks a button:

  • Write a function that takes a variable and store the following using Math.floor() and Math.random() method.
const handleClick = () => {
  const randomIndex = Math.floor(Math.random() * quotes.length);
}

The Math.random() generates a random number, which can be used to select a random quote from an array of quotes.

  • Assign the quotes from the array to the updated setSelectedQuote variable, which was defined using useState().

Here's an example of how you can write the function:

const handleClick = () => {
  const randomIndex = Math.floor(Math.random() * quotes.length);
  setSelectedQuote(quotes[randomIndex]);
}

This function enables the user to click the button and generate a random quote.

Result

Final Result

Upon clicking the button, the function handleClick() is called, triggering the selection of a random quote from the quotes array and assigning it to the selectedQuote variable.

Loading Aspect

One of the dependencies included in this project is the react-loader-spinner package.

to use it.

  • Ensure react-loader-spinner is installed.

  • Import it to where it will be used; for this project, it was imported at the Home.jsx.

A video demonstrating how it is used

In the Home.jsx file:

The ternary "!" operator can be used as a toggle to change the value of a boolean variable from true to false or vice versa. In the context of the code, the "!" operator is used to toggle the value of the isLoading variable. When isLoading is true, the spinner is displayed on the webpage. Once data is retrieved from the API, isLoadings is toggled to false, causing the spinner to disappear and BookList.jsx to be displayed.

This is an efficient way to manage the state of the component and display a loading spinner until the data is retrieved from the API. Once the data is available, the spinner is automatically removed and the BookList component is displayed, providing a better user experience.

import { useState } from 'react';
import BookList from './BookList';
import Input from './Input';
import Navbar from './Navbar';
import { Circles } from 'react-loader-spinner';

const Home = () => {
  const [term, setTerm] = useState('Anything');
  const [isLoading, setIsLoading] = useState(true);

  return (
    <div>
      <>
        <Navbar />
        <div className='header'>
          <div className='overlay'>
            <h2 className='Heading-text'>Books on {term}</h2>
            <p>
            “Reading is an act of civilization; it’s one of the greatest acts
            of civilization because it takes the free raw material of themind
            and builds castles of possibilities.”
            </p>
            <Input searchBooks={(search) => setTerm(search)} />
          </div>
        </div>
        <div>
          {isLoading && (
            <div>
              <Circles
                height='50'
                width='50'
                color='brown'
                ariaLabel='circles-loading'
                wrapperStyle={{}}
                wrapperClass=''
                visible={true}
              />
            </div>
          )}
        </div>
        <div >
          {!isLoading && <BookList books={books} />}
        </div>
        {error && (
          <div >
            {error}
          </div>
        )}
      </>
    </div>
  );
};

export default Home;

In the event of a network issue, the data cannot be retrieved from the API. An error message will be defined using useState().

const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);

Custom hook(optional)

In developing a web application, the code for a particular component can quickly become cluttered with various functions and logic, which can make it challenging to read and understand. One way to avoid this clutter is by using custom hooks.

In React, any custom hook defined is declared with the word "use".

To create a custom hook

  • Create the component UseFetch.jsx

  • Copy and paste all the logic.
import React, { useEffect, useState } from 'react';

const UseFetch = (url) => {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then((res) => {
        return res.json();
      })
      .then((data) => {
        // console.log(data.items);
        setData(data.items);
        setIsLoading(false);
      })
      .catch((err) => {
        setError(err.message);
        setIsLoading(false);
      });
  }, [url]);

  return { data, isLoading, error };
};

The URL functions as the endpoint, thus making the component reusable. In my project, I employed it in Home.jsx and just returned the data that was defined to the specific location where it was needed.

Home.jsx

import react, { useState } from 'react';
import BookList from './BookList';
import Input from './Input';
import Navbar from './Navbar';
import UseFetch from './UseFetch';
import { Circles } from 'react-loader-spinner';

const Home = () => {
  const [term, setTerm] = useState('Anything');
  const {
    data: books,
    isLoading,
    error,
  } = UseFetch(
    `https://www.googleapis.com/books/v1/volumes?q=${term}&key=${
      import.meta.env.VITE_SOME_VALUE
    }`
  );

  return (
    <div>
      <>
        <Navbar />
        <div className='header'>
          <div className='overlay'>
            <h2 className='Heading-text'>Books on {term}</h2>

            <p>
            “Reading is an act of civilization; it’s one of the greatest acts
            of civilization because it takes the free raw material of themind
            and builds castles of possibilities.”
          </p>
            <Input searchBooks={(search) => setTerm(search)} />
          </div>
        </div>
        <div>
          {isLoading && (
            <div>
              <Circles
                height='50'
                width='50'
                color='brown'
                ariaLabel='circles-loading'
                wrapperStyle={{}}
                wrapperClass=''
                visible={true}
              />
            </div>
          )}
        </div>

        <div>
          {!isLoading && <BookList books={books} />}
        </div>
        {error && (
          <div>
            {error}
          </div>
        )}
      </>
    </div>
  );
};

export default Home;

Conclusion

In conclusion, we've learned how to populate an API and render it dynamically on a webpage using React. Specifically, we've covered topics such as creating components, using state and props, making API requests, and handling user events. With these skills, you can build web applications that provide a great user experience.

This concludes the tutorial series.

source code

Book-Web-App

Resources

Looking for an article on APIs to use in your projects? You may find it helpful to read through the following article:

Feel free to connect with me on Twitter and LinkedIn.

Thank you for reading đź’–.

Did you find this article valuable?

Support Ijeoma Igboagu by becoming a sponsor. Any amount is appreciated!

Â