DEV Community

Farhana
Farhana

Posted on • Edited on

How to build a book recommender in React.js

If you were looking to learn a new skill, you’re in luck because I’m looking to give technical blogging a try.

This will be a tutorial on how to build a Book Recommender with React.js and the NYTimes API. Of course, you don’t have to use the NYTimes API. There are plenty of others for bibliophiles to use like Google Books API and Good Reads API. I will also be using Visual Studio Code as my text editor but feel free to use anything else if this one is not your cup of tea.

Step 1: Starting with React.js

React is a JavaScript framework created by Facebook. It’s widely used in many different companies so it’s great to get some practice with the framework for your own growth and learning.

I’m assuming you already have a package manager installed and know how to use the command line. I’m on a handy dandy pc computer running Windows 10 so I use Windows Powershell as my terminal.

At the time of this post, I'm using npm version 6.14.2. Go ahead and run the command npm install -g npm to update npm if this is your chosen package manager.

After changing to the directory where you want your source code to be in,
type the following on your terminal

npx create-react-app my-app
cd my-app
npm start
Enter fullscreen mode Exit fullscreen mode

This is building a new single page application in React. Your app name doesn’t have to be “my-app”; it can be whatever you want to call it. I named my app book recommender. The npm start command should open your default web browser and display the default code in localhost:3000

In your src folder, you’ll see the file App.js. Go ahead and open your favorite code editor to start editing this file. You’ll see the template code that React has provided. Delete this code and paste the following:

import React from 'react';
import './App.css';
import Header from './Components/Header';
import Book from './Components/Book';
import Footer from './Components/Footer';

class App extends React.Component {
    render() {
        return (
            <div className="App">
                <Header/>
                <Book/>
                <Footer/>
            </div>
        )
    }
}

export default App
Enter fullscreen mode Exit fullscreen mode

Save this file and look at your localhost. You’ll see an error
Failed to compile
./src/App.js
Module not found: Can't resolve './Components/Book

Right now, we’re trying to import files that don’t exist yet. To keep our code organized, I have a few files that display different portions of the web app.

Next, create a folder within your src folder called Components. Then, create two files within the Components folder called Header.js and Footer.js

Within Header.js:

import React from "react"

class Header extends React.Component {
    render() {
        const title = "Book Recommender"
        return(
            <div>
                <h1>{title}</h1>
                <h2>Which book should you read next?</h2>
            </div>
        )
    }

}

export default Header
Enter fullscreen mode Exit fullscreen mode

Within Footer.js:

import React from "react"

class Footer extends React.Component {
    render() {
        return(
            <footer>Made with love</footer>
        )
    }    
}

export default Footer
Enter fullscreen mode Exit fullscreen mode

Feel free to alter the text to whatever you want. And always remember to save your files.

Next, within the Components folder, create a new file called Book.js. This file will be doing the most heavy lifting in our application.

Let’s start compiling this file in parts.

Copy and paste this skeleton code into Book.js:

import React from 'react';
import config from '../config';

class Book extends React.Component {

}

export default Book
Enter fullscreen mode Exit fullscreen mode

Right now, the config file doesn’t exist yet. We’ll get to that later.

Here, we declared the Book class as a React component and exporting it on the last line so we can then successfully call this component back in the App.js file.

Next, under the class Book declaration, create the constructor method

    constructor(props) {
      super(props);
      this.state = {
        error: null,
        isLoaded: false,
        items: [],
        rank: 0,
        title: "",
        author: "",
        book_image: "",
        amazon_product_url: ""
      };
    }
Enter fullscreen mode Exit fullscreen mode

This is telling your program which data to expect by initializing the state and lets us use that data.

Under the closing curly bracket of your constructor method, hardcode your API URL

    URL = 'https://api.nytimes.com/svc/books/v3/lists/current/hardcover-fiction.json?api-key=';
    mykey = config.MY_KEY;
Enter fullscreen mode Exit fullscreen mode

Now, we’re going to create one final file. This file will be outside of your Components folder but still within the src folder. Name this file config.js and paste the following code into the file:

var config = {
    MY_KEY : 'InsertYourKeyHere'
}
export default config
Enter fullscreen mode Exit fullscreen mode

Alright! Hope everything was smooth so far. We’re going to take a little break from our text editor and create the API key that we need.

Step 2: NYTimes Book API

Next, head on over to the New York Times Dev Portal
Create a developers account
After you successfully follow their steps to create an account, click your username/email in the upper right to see the drop down menu.
Click Apps in this drop down menu.
On the far right, click the +NEW APP button.
Give a name and scroll down until you see the Books API card. Toggle this API to be enabled.
Scroll back up and click Create on the top right.
When your app is created, you will see your API key. Hover over the generated code under Key and click the Copy to clipboard button that appears. Your key is now copied.

Back to VSCode! Within your config.js file, delete the InsertYourKeyHere text within the single quotes and paste your key. Save your file.

Now we have the config.js file that is imported at the top of our Book.js file. Generally, you want all of your top secret data in a config file. It’s good practice to not hardcode endpoints/API URLS within files but to call your config file to use this sensitive data. You should also always add your config files to gitignore because you don’t want to have your API secrets exposed when you’re using version control. If you have no idea what I’m talking about, no worries! This can be a different blogpost for another day.

Add the rest of this code under your mykey variable:

componentDidMount() {
        fetch(this.URL + this.mykey)
        .then(res => res.json())
        .then(
          (result) => {
            this.setState({
              isLoaded: true,
              items: result.results.books
            });
          },
          // Note: it's important to handle errors here
          // instead of a catch() block so that we don't swallow
          // exceptions from actual bugs in components.
          (error) => {
            this.setState({
              isLoaded: true,
              error
            });
          }
        )
    }

    handleGetRandomRank = (e) => {
      this.setState({
        rank: Math.floor(Math.random() * 15) + 1
      })
    };

    render() {
      let { error, isLoaded, items, rank } = this.state;
      if (error || !items[rank]) {
        return <button className="" onClick={this.handleGetRandomRank}>Give me a Best Seller</button>

      } 
      else if (!isLoaded) {
        return <div>Loading...</div>;
      }
      //if user is first landing on the page, only display button 
      else if (!rank) {
        return <button className="" onClick={this.handleGetRandomRank}>Give me a Best Seller</button>
      }
      else {
        return (
          <div className="content">
            <div>
              <button className="" onClick={this.handleGetRandomRank}>Give me a Best Seller</button>
            </div>
            <div className="bookCoverStyling">
              <img src={items[rank].book_image} alt="Book cover"/>
            </div>
            <div className="titleStyling">
              {items[rank].title}
            </div>
            <div className="authorStyling">
              by: {items[rank].author}
            </div>
            <div className="descriptionStyling">
              {items[rank].description}
            </div>
            <a href={items[rank].amazon_product_url}>Amazon URL</a> 
          </div>
        );
      }
    }
Enter fullscreen mode Exit fullscreen mode

That’s a lot of code, I know!

Here’s the breakdown of the code:

  • The componentDidMount method is calling our NYTimes API and turning the response data into readable json data that we can use. Then for each book object given to us from NYTimes, we store the data into an items array.
  • The handleGetRandomRank method is a custom method. The “recommend” portion of my book recommender is simply a random number, which is used to get a random book from our items array.
  • The render method is a required method whenever you make a subclass of React.Component. It displays your XML-like code. Inside this render method, I have various if statements. If there’s an error, we’re just displaying a button that says Give me a Best Seller. When this button is clicked, it executes the handleGetRandomRank method to generate a random number. If the page is still loading, we’re just going to display some text that says Loading. Next, if the user is entering the website for the first time, we just want to display the Give me button. And finally, we’re displaying the data we received from the API and displaying it on our page. I’ve included the book cover image, the title, author, description and purchase URL. There’s a lot of data being given from NYTimes so feel free to pick different things to display or even use a different endpoint!

If everything went well, you should save all of your files and refresh your localhost, assuming you still have npm start running in your terminal/command line, and see that the error is no longer there but you should see the app you just created.

I’m not great at CSS so feel free to add your own styling and personality to the design of this web app.

Here’s my live version and GitHub repo of this project!

I hope you gained something valuable from my tutorial, whether it’s adding a new tool under your belt or having another project on your resume!
Let me know what you think or have any suggestions or corrections in the comments. I’m always looking to improve.

Happy learning.

Top comments (4)

Collapse
 
sasagichuki profile image
Charles Gichuki

How do i solve this small problem

Line 5:21: Parsing error: Unexpected token, expected ";"

componentDidMount() {
^
fetch(this.URL + this.mykey)
.then(res => res.json())
.then(

Collapse
 
farhanaxmustafa profile image
Farhana • Edited

Hey Charles! Thanks for reaching out. There might be a missing closing bracket above your componentDidMount method. It looks like Line 5 should be constructor(props). Ensure that it has a opening curly bracket and closing curly bracket like this:

  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      items: [],
      rank: 0,
      title: "",
      author: "",
      book_image: "",
      amazon_product_url: ""
    };
  }

  URL = 'https://api.nytimes.com/svc/books/v3/lists/current/hardcover-fiction.json?api-key=';
  mykey = config.MY_KEY;

  componentDidMount() {
Collapse
 
nickytonline profile image
Nick Taylor

Congrats on your first post!

1st place in Mariokart

Collapse
 
farhanaxmustafa profile image
Farhana

Thank you!