Building A Scalable Random Quote App In React
Tags: react, javascript, freecodecamp, beginners
Published: 2019-03-30 22:52:26

---
title: Building A Scalable Random Quote App In React
published: true
description: 
tags: react, javascript, freecodecamp, beginners
cover_image: https://thepracticaldev.s3.amazonaws.com/i/5x0209l612xovtypmufm.png
---
Side project and real-world app are very fascinating terms for programmers, but building a side project is not a piece of cake, we have to build some projects first to gain some certain expertise before starting our own project. Freecodecamp is very helpful in this regard. So today we are going to solve freecodecamp's Random Quote Machine challenge using React.

##Let's plan the app first##

We have divided this small app into two steps.
In first step we will design the whole app in a single component. As soon it will fulfill the purpose we will move towards second step and divide the app into small separate components that would be helpful if the app gets bigger in future. 

###Component Setup###

Before writing any logic we will set up our component and initialize our state object with quote and author values. The value will be an empty string for now.

```react
import React, { Component } from 'react'

class RandomQuote extends Component {
   constructor(props) {
      super(props)
      this.state = {
         quote: '', //for quote
         author: '' //for author
      }
   }

   render() {
      return (
         <div id='wrapper'>
            <h1 className='title'>Random Quote App</h1>
         </div>
      )
   }
}

export default RandomQuote
```
###Package for API request###

We are going to use <code>axios</code> for <code>API</code> request. Its promise based and makes the <code>Api</code> request easier, shorter and cleaner.

We will call our <code>API</code> in <code>componentDidMount</code> lifecycle method.

You may think why <code>componentDidMount</code>?

So here we have to clear a concept first, some newbie may not aware of this.

### Concepts ###

In our <em>class based component</em> we have some predefined <em>methods</em>, and every method has a certain feature and time for execution. 
<em>(Will write a detailed article on react life cycle methods)</em>

<blockquote>
**First:** constructor runs<br /> 
**Second:** render runs<br /> 
**Third:** componentDidMount runs
</blockquote>

We can confirm it just by console logging on all three methods and see what runs first... Here is the boilerplate for that.

```react
import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props)

    console.log('constructor runs')
  }

  componentDidMount() {
    console.log('componentDidMount runs')
  }

  render() {
    console.log('render method runs')
    return (
      <div>
        <h1>Hello</h1>
      </div>
    );
  }
}

export default App;
```
if you inspect you will see this in consoles.

<blockquote>
constructor runs App.js:7<br />  
render method runs App.js:15<br />  
componentDidMount runs App.js:11
</blockquote>

###Calling API in componentDidMount###

So we have seen componentDidMount runs after default render method. So it is best place for API call.

```react
componentDidMount() {
      this.getQuote()
   }

   getQuote() {
      let url = 'https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'

      axios.get(url)
         .then(res => console.log(res))
   }
```
 
If we inspect we can see API data in console. That means we have successfully called an <code>API.</code>
 
Now we will alter the state object with <code>setState</code> property and make <em>quote</em> and <em>author</em> value equal to some data we are getting from <em>api</em>.

<em>Its time to write some logic</em> 

## Logic 1 : Pick a random quote from API ##

If we can figure out how to get a random element from array we can write logic for this. We have array of quotes that further have quote and author keys, as an element.

We know in order to get random number in Javascript we use built in <code>Math.random()</code> <em>function</em> and to get data from certain length we will extend it like this 
```javascript
Math.floor(Math.random() * data.length)
```
<em>Math.floor()</em> just round a number downward to its nearest integer.

This will give us random number from 0 to the length of array and we stored it in a variable <code>quoteNum</code>. 

What if treat <code>quoteNum</code> as an index? we will get a random element from <em>quotes array</em>.

```react
import React, { Component } from 'react'
import axios from 'axios'


class RandomQuote extends Component {
   constructor(props) {
      super(props)
      this.state = {
         quote: '',
         author: ''
      }
   }

   componentDidMount() {
      this.getQuote()
   }

   getQuote() {
      let url = 'https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'

      axios.get(url)
         .then(res => {
            let data = res.data.quotes
            let quoteNum = Math.floor(Math.random() * data.length) //quote number
            let randomQuote = data[quoteNum] //actual quote

            this.setState({
               quote: randomQuote['quote'],
               author: randomQuote['author']
            })
         })
   }

   getNewQuote = () => {
      this.getQuote()
   }

   render() {
      const { quote, author } = this.state
      return (
         <div id='wrapper'>
            <h1 className='title'>Random Quote App</h1>

            <div id='quote-box'>
               <div id='text'><p>{quote}</p></div>
               <div id='author'><h5>{author}</h5></div>
            </div>
         </div>
      )
   }
}

export default RandomQuote
```

You will observe that you won't see the data for couple of milliseconds as soon the app runs, because it takes time to get data from api. 
As soon the request become successful it will store new values in <code>state</code> using <code>setState</code> and our <code>DOM</code> will be updated with new data. 

Now we are only left with <em>New Quote</em> and <em>Twitter</em> share feature. 

## Logic 2: Share on Twitter ##

Twitter share icon will act as anchor tag, that will take us somewhere on clicking the <em>icon</em>. So we have to figure out the dynamic <code>href</code> in anchor tag.
<code>https://twitter.com/intent/tweet?text=</code> takes us to our Twitter account with tweet box opened, what we write after this <em>url</em> it will appear in tweet box.
So we want quote and author data in tweet box. We know quote and author's data is in the <em>state</em> so by using <code>ES6 template literals</code> we can concatenate <em>quote</em> and <em>author</em> with <em>url</em>

This is how our finished app looks like now. 
 
```react
import React, { Component } from 'react'
import axios from 'axios'

class RandomQuote extends Component {
   constructor(props) {
      super(props)
      this.state = {
         quote: '',
         author: ''
      }
   }

   componentDidMount() {
      this.getQuote()
   }

   getQuote() { 
      let url = 'https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'

      axios.get(url)
         .then(res => {
            let data = res.data.quotes
            let quoteNum = Math.floor(Math.random() * data.length)
            let randomQuote = data[quoteNum]

            this.setState({
               quote: randomQuote['quote'],
               author: randomQuote['author']
            })
         })
   }

   getNewQuote = () => { //will be called on clicking the New Quote button
      this.getQuote()
   }

   render() {
      const { quote, author } = this.state //Destructuring
      return (
         <div id='wrapper'>
            <h1 className='title'>Random Quote App</h1>

            <div id='quote-box'>
               <div id='text'><p>{quote}</p></div>
               <div id='author'><h5>{author}</h5></div>

               <div id='buttons'>
                  <a id='tweet-quote' href={`https://twitter.com/intent/tweet?text=${quote} ${author}`} target='_blank' title="Post this quote on twitter!">
                     <span>
                        <i className="fab fa-twitter twitter-icon" /> //fontawesome twitter icon
                     </span>
                  </a>
                  <button id='new-quote' className='buttons' onClick={this.getNewQuote}>New Quote</button>
               </div>
            </div>
         </div>
      )
   }
}

export default RandomQuote
```
and for <em>New Quote</em> button we are calling the <code>getQuote</code> method inside <code>getNewQuote</code> and binding it <code>onClick props.</code>

## Styling ##

We need to style three things
<blockquote>
1. Design the background<br/>
2. Design the Quote Box<br/>
3. Design the buttons
</blockquote>

This article is not about styling. If you didn't understand anything you can ask in comment section. 
I have added media queries to make it responsive when it comes to small screens.

```css
@import url('https://fonts.googleapis.com/css?family=Josefin+Sans|K2D');

body {
  background: linear-gradient(90deg, lightgreen, lightblue);
  font-family: 'K2D', sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  height: calc(100vh - 100px);
  overflow-y: hidden;
}

.title {
  text-align: center;
  font-weight: 500;
}

#quote-box {
  width: 400px;
  margin: 0 auto;
  padding: 1px 15px;
  font-weight: 550;
  font-size: 22px;
  background: linear-gradient(35deg, #CCFFFF, #FFCCCC);
  text-align: center;
  border-radius: 20px;
  box-shadow: 0px 0px 2px 1px gray;
}

#text p {
  margin-block-start: 0.5em;
  margin-block-end: 0.5em;
}

#author h5 {
  margin-block-start: 1em;
  margin-block-end: 1em;
}

#buttons {
  display: flex;
  justify-content: space-between;
}

.twitter-icon {
  color: #1DA1F2
}

.button {
  font-family: 'K2D', sans-serif;
  font-weight: 500;
  font-size: 1rem;
  padding: 5px;
  border-radius: 50em;
  box-shadow: 0px 0px 3px .5px rgb(82, 81, 81);
  border: 0;
  margin-bottom: 10px;
}

.button:focus {
  outline: none;
  border: none;
}

@media only screen and (max-width: 450px) {
  .title {
    font-size: 22px;
  }
  #quote-box {
    width: 270px;
  }
}
```
Here we are done with first step.

##Let's talk about scaling the app##

Remember, we always structure our project in a way that its easier to grow, read and maintainable. 

##Reusable Quote Box##

Assume that we want to add more screens/routes to the app later, and we want to use same quote box but with different text/data. So we will make a separate component <em>QuoteBox</em> for that. Similar thing we will do with <em>New Quote</em> and <em>Share buttons.</em>

```react
// Quote Box component
const QuoteBox = ({ quote, author }) => { //destructuring
   return (
      <React.Fragment>
         <div id='text'><p>{quote}</p></div>
         <div id='author'><h5>{author}</h5></div>
      </React.Fragment>
   )
}
``` 
Here we are getting author and quote values from <em>RandomQuote component</em> via <code>props.</code>

##Reusable Button##

Suppose this is a client project and he changed his mind and asked you instead of having one New Quote button he wants to have two buttons, one for Next Quote and one for Previous Quote. 

So it's better to make one reusable button, we will use <em>Button component</em> wherever we want the same button.

```react
//Button component
const Button = ({ onClick, title }) => {
   return (
      <button className='button' id='new-quote' onClick={onClick}>{title}</button>
   )
}
```

##Reusable Share Buttons##

What if we want to add Facebook, Instagram and whatsapp share later. They would share the same styling but different <code>props</code>. So its better to write it in a separate file, will be easier for maintenance. 

```react
// Social Share component
const TwitterShare = ({ quote, author }) => {
   return (
      <React.Fragment>
         <a href={`https://twitter.com/intent/tweet?text= ${quote} ${author}`} target="_blank" title="Post this quote on twitter!" id='tweet-quote'>
            <i className="fab fa-twitter twitter-icon" />
         </a>
      </React.Fragment>
   )
}
```
This is how our random quote class looks like, isn't it cleaner now?

```react
class RandomQuote extends Component {
   constructor(props) {
      super(props)
      this.state = {
         quote: '',
         author: ''
      }
   }

   componentDidMount() {
      this.getQuote()
   }

   getQuote() {
      let url = 'https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'

      axios.get(url)
         .then(res => {
            let data = res.data.quotes
            let quoteNum = Math.floor(Math.random() * data.length)
            let randomQuote = data[quoteNum]

            this.setState({
               quote: randomQuote['quote'],
               author: randomQuote['author']
            })
         })
   }

   getNewQuote = () => { //will be called on clicking the New Quote button
      this.getQuote()
   }

   render() {
      const { quote, author } = this.state
      return (
         <div id='wrapper'>
            <h1 className='title'>Random Quote App</h1>

            <div id='quote-box'>
               <QuoteBox quote={quote} author={author} /> //passing data via props to QuoteBox component

               <div id='buttons'>
                  <TwitterShare quote={quote} author={author} />
                  <Button id='new-quote' title='New Quote' onClick={this.getNewQuote} />
               </div>
            </div>
         </div>
      )
   }
}
```

This article was bit longer, hope you followed along and learned something new.

In next tutorial we will build the same app in <em>React Native</em> with different design and concept.

One more thing, we used a pre-built API, so we will design our own <em>rest API</em> using <em>node, express and MonogDB</em> for quotes.

<a href="https://codepen.io/Basit600/pen/qvwjbg?editors=0011" target="_blank">Here</a> is the codepen link of the project.