DEV Community

Cover image for Quick React Project: Calculate your TBR Goal
Kailana Kahawaii
Kailana Kahawaii

Posted on

Quick React Project: Calculate your TBR Goal

I like to start my year off by setting a reading goal. This relatively quick project will calculate how long it’ll take to complete your reading challenge based on words read per minute and the average length of a book.

This project is perfect for those just starting React, or those who want to refresh their skills after taking a break.

An example of the finished project

Getting Started

Create a new project by using the Create New React App command.

$ npx create-react-app reading-challenge
Enter fullscreen mode Exit fullscreen mode

After your project has been created, create a components folder in src and add two new files: Form.js and ReadingLog.js.

file structure showing readinglog and form in components folder

ReadingLog will be responsible for some of the styling and some of the content, but most of the meat of the project will be in Form.

You can also feel free to delete the files you don’t need.

Our display file

I’ve kept the display file super short. It displays a title and the form component.

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

function Readlog(){

        return(
        <div>
                 <h1>How long will my TBR take?</h1>
                 <Form/>
        </div>
        )
}

export default Readlog;
Enter fullscreen mode Exit fullscreen mode

This a good file to add styling to as you can wrap the form and other elements in it.

Figuring out the form

My first idea was to differentiate books based on children’s fiction, young adult fiction, and general literature. However, that kind of information can be hard to parse, even if you have a CSV file handy.

Instead, I went for an MVP version of that idea.

I researched the average length of a novel, which is 90,000 words. With this information, I only needed to figure out my WPM or words per minute. I used this speed reading test to calculate my speed.

With that in mind, the formula would look something like this:

minutes = Number of books x 90000 / words per minute

For the above formula, what kind of information would a user need to disclose?

  • number of TBR books
  • reading speed

This information can be held in state.

class Form extends React.Component{
    constructor(){
        super()
        this.state={
            books: 0, 
            wordsPerMinute: 0
        }
    }
[]
}
Enter fullscreen mode Exit fullscreen mode

Now, we’ll need a form for the user to put this information into. Since both of these variables are numbers, I’ve used type=“number” on the input form.

    render(){
        return(
        <div>
            <form>
                <label>Number of books on your TBR</label>
                <input
                placeHolder="enter the number of books"
                type="number"
                name="book"
                value={this.state.books}
                onChange={ (e) => this.handleChange(e) }
                />
                <label>Reading Speed (wpm)</label>
                <input
                type="number"
                name="wordsPerMinute"
                value={this.state.wordsPerMinute}
                onChange={ (e) => this.handleChange(e) }
                />
            </form>
        </div>
        )
    }

Enter fullscreen mode Exit fullscreen mode

You’ll notice that there’s an onChange function being referenced here, so it’s a good Idea to write one.

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

Enter fullscreen mode Exit fullscreen mode

Fun fact: the brackets around [e.target.name] tells react that this code refers to a dynamic key name.

So we have a form, and we can change the state using the form.

Now, we need to actually calculate the number of minutes, hours, and days it’ll take to read all those books based on the user’s reading speed.

Time conversion

We have the reader’s wpm represented by this.state.wordsPerMinute, and we have their TBRs based on this.state.books.

let wpm = this.state.wordsPerMinute
let books = this.state.books
Enter fullscreen mode Exit fullscreen mode

Glancing at our formula again, in order to calculate the minutes, we’ll need to multiply the number of books by 90000 and divide by the wpm. I’ve also used Math.floor so we get nice, whole numbers. This will give us the minutes it’ll take to read all those books (give or take).

 let minutes = Math.floor((books * 90000)/wpm) || 0
Enter fullscreen mode Exit fullscreen mode

Note that || 0 will give the user a 0 instead of NaN value if they enter 0 into the input form.

With this information, we only need to do a couple more time conversions to calculate the days and hours.

    let hours = Math.floor(minutes/60) || 0
     let days = (hours/24).toFixed(1) || 0

Enter fullscreen mode Exit fullscreen mode

Then, with those times, we can add a bit of dialogue to the state, that will be dynamically rendered when the user clicks a button. To do this we’ll need to write it into our function.

   this.setState({
            time:`It'll take about ${minutes} minutes to get through your TBR list if you read continuously. That translates to ${hours} hours or about ${days} days.`
        })
Enter fullscreen mode Exit fullscreen mode

And we’ll also need to include it in our state:


class Form extends React.Component{
    constructor(){
        super()
        this.state={
            books: 0, 
            wordsPerMinute: 0, 
       time: 
        }
    }

Enter fullscreen mode Exit fullscreen mode

The whole function looks like this:

  calculateTime = () => {
        let wpm = this.state.wordsPerMinute 
        let books = this.state.books
        let minutes = Math.floor((books * 90000)/wpm) || 0
        let hours = Math.floor(minutes/60) || 0
        let days = (hours/24).toFixed(1) || 0

        this.setState({
            time:`It'll take about ${minutes} minutes to get through your TBR list if you read continuously. That translates to ${hours} hours or about ${days} days.`
        })
    }

Enter fullscreen mode Exit fullscreen mode

You can tie the form to an onSubmit function…

<form onSubmit={(e) => this.handleSubmit(e)}>

[]

<input type=submit/>
Enter fullscreen mode Exit fullscreen mode

…and write out the submit function.


handleSubmit = (e) => {
        e.preventDefault()
        this.calculateTime()
    }
Enter fullscreen mode Exit fullscreen mode

Keep in mind that e.preventDefault() will prevent the form from doing an annoying page refresh.

Now, all that’s left to do, is add the dialogue from state (we’ll use a ternerary to conditionally render it after the user clicks the button).


<p>{this.state.time ? this.state.time : null}</p>
Enter fullscreen mode Exit fullscreen mode

Altogether, our file will look like this

import React from 'react';

class Form extends React.Component{
    constructor(){
        super()
        this.state={
            books: 0, 
            wordsPerMinute: 0,
            time: ''
        }
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

    calculateTime = () => {
        let wpm = this.state.wordsPerMinute 
        let books = this.state.books
        let minutes = Math.floor((books * 90000)/wpm) || 0
        let hours = Math.floor(minutes/60) || 0
        let days = (hours/24).toFixed(1) || 0

        this.setState({
            time:`It'll take about ${minutes} minutes to get through your TBR list if you read continuously. That translates to ${hours} hours or about ${days} days.`
        })
    }


handleSubmit = (e) => {
    e.preventDefault()
    this.calculateTime()
}

    render(){
        return(
        <div>
           <form onSubmit={(e) => this.handleSubmit(e)}>
                <label>Number of books on your TBR</label>
                <input
                placeHolder="enter the number of books"
                type="number"
                name="book"
                value={this.state.books}
                onChange={ (e) => this.handleChange(e) }
                />
                <label>Reading Speed (wpm)</label>
                <input
                type="number"
                name="wordsPerMinute"
                value={this.state.wordsPerMinute}
                onChange={ (e) => this.handleChange(e) }
                />
                 <input type="submit"/>
            </form>
            <p>{this.state.time ? this.state.time : null}</p>
        </div>
        )
    }
}

export default Form


Enter fullscreen mode Exit fullscreen mode

Ideas for expansion

This works great, but I can’t help but feel that the number it gives could be more accurate. This stackoverflow discussion about book word counts seems like a pretty good place to start.

Summary

Even though it’s going on 9 months since I first picked up React, there’s still so much to learn. Quick projects are a good way to practice and improve on skills without feeling overwhelmed. It was also useful to review syntax like brackets around event.target.name.

Hope your reading challenges go well!

Here's my goal. I'd better get reading. 😂 My reading goal (it will take 48 days)

Top comments (0)