React Random Quote Generator

One of the most fun projects I have done lately has been a react random quote generator. I did this as a way to sharpen my React.js skills through the most excellent freeCodeCamp.org platform. If you haven’t already, you really should head over there and check out some of the challenges they post. They look good on your resume, and most importantly you learn a lot.

In this lab we will cover the basics of how to get React going in CodePen. This was a fairly easy project that we can do in just two components (counting the App component). Here is a link to my solution on CodePen if you would like to take a look. https://codepen.io/claytonkreisel/pen/abvKmwW

Now some of you are wondering what exactly you are looking at here. If that is you, then you are probably somewhat new to React. Let me start by saying this. This lab is not the place for you to learn how to build a React.js project. That is covered in many places on the web, and I would urge you to check it out elsewhere. So, if you are not familiar with React.js, I would first point you in the direction of this video to help you get started https://www.youtube.com/watch?v=DLX62G4lc44.

So let’s take a look at what is happening in my code. First I would ask you to simply copy over the SCSS. I am not going to focus on that in this lab. We are going to focus on the JS pane of the CodePen and talk about the React. Below is a video walking you through every step. The SCSS and other parts of the solution are also listed below.

HTML

<div id="root"></div>

CSS (SCSS)

@import url('https://fonts.googleapis.com/css?family=Quicksand:400,600&display=swap');

$defaultFont: 'Quicksand', sans-serif;;

$green: #23a186;
$orange: #bf8622;
$red: #9c3636;
$blue: #253ba8;
$purple: #7a2d7d;
$gray: #3b3b3b;
$default: $gray;

$colors: (
  "green": $green,
  "orange": $orange,
  "red": $red,
  "blue": $blue,
  "purple": $purple,
  "gray": $gray
);

#app{
  background: $default;
  height: 100%;
  width: 100%;
  position: fixed;
  top: 0;
  left: 0;
  font-family: $defaultFont;
  transition: all 200ms cubic-bezier(0.000, 0.000, 0.580, 1.000);
  
  @each $color, $hex in $colors {
    &.#{$color}{
      background: $hex;
      
      #quote-box{
        border-color: darken($hex, 20%);
        background: lighten($hex, 10%);
        
        button#new-quote, a#tweet-quote{
          background: lighten($hex, 25%);
          border: 1px solid darken($hex, 5%);
          
          &:hover{
            background: lighten($hex, 15%);
          }
        }
      }
    }
  }
  
  #quote-box{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    max-width: 800px;
    width: 98%;
    max-height: 500px;
    min-height: 150px;
    height: auto;
    border: 1px solid darken($default, 5%);
    border-radius: 10px;
    color: #f7f7f7;
    background: lighten($default, 10%);
    transition: all 200ms cubic-bezier(0.000, 0.000, 0.580, 1.000);
    
    button#new-quote, a#tweet-quote{
      position: absolute;
      bottom: 15px;
      left: 15px;
      background: lighten($default, 25%);
      border: 1px solid darken($default, 5%);
      border-radius: 4px;
      padding: 8px 12px;
      text-transform: uppercase;
      color: darken($default, 25%);
      font-family: $defaultFont;
      font-size: 1em;
      transition: all 200ms cubic-bezier(0.000, 0.000, 0.580, 1.000);
      cursor: pointer;
      
      &:hover{
        background: lighten($default, 15%);
      }
    }
    
    a#tweet-quote{
      text-decoration: none;
      right: 15px;
      left: auto;
    }
    
    #quote-content{
      max-height: calc(100% - 140px);
      height: auto;
      position: relative;
      width: calc(100% - 180px);
      margin: 60px 90px 80px;
      
      #text{
        font-size: 2em;
        position: relative;
        
        &:before{
          position: absolute;
          content: '"';
          font-size: 5em;
          opacity: .2;
          left: -60px;
          top: -50px;
          
        }
      }
      
      #author{
        position: relative;
        text-align: right;
        font-style: italic;
        font-size: 1.2em;
        padding-top: 30px;
        
        &:before{
          content: "- ";
        }
      }
    }
  }
}

JavaScript (Babel)

//App Component
class App extends React.Component {
  constructor(){
    super();
    this.state = ({
      color: "purple"
    });
    
    this.handleColorChange = this.handleColorChange.bind(this);
  }
  
  //Change the color of the App
  handleColorChange(color){
    this.setState({
      color: color
    });
  }
  
  render() {
    return (
      <div id="app" className={this.state.color}>
        <QuoteBox onChangeAppColor={this.handleColorChange} color={this.state.color} />
      </div>
    );
  }
}

//Quote Box Component
class QuoteBox extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      quoteText: "",
      quoteAuthor: "",
      curColor: this.props.color,
      tweetUrl: "https://twitter.com/intent/tweet/?text="
    }
    
    this.getNewQuote();
    
    //Bind Handles and Functions
    this.getNewQuote = this.getNewQuote.bind(this);
    this.handleNewQuote = this.handleNewQuote.bind(this);
  }
  
  //Get a quote and author from an API and set state
  getNewQuote(){
    const app = this;
    fetch('https://type.fit/api/quotes')
    .then((response) => response.json())
    .then((data) => {
      const index = Math.floor(Math.random() * data.length);
      app.setState({
        quoteText: data[index].text,
        quoteAuthor: data[index].author,
        tweetUrl: "https://twitter.com/intent/tweet/?text=" + data[index].text.replace(/ /g, "+")
      });
    });
  }
  
  //Handles the new quote button click
  handleNewQuote(){
    const colors = ["gray", "blue", "purple", "red", "orange", "green"];
    let color = colors[Math.floor(Math.random() * colors.length)];
    while(color == this.state.curColor){
      color = colors[Math.floor(Math.random() * colors.length)];
    }
    this.setState({
      curColor: color
    });
    this.props.onChangeAppColor(color);
    this.getNewQuote();
  }
  
  render() {
    return (
      <main id="quote-box">
        <div id="quote-content">
          <div id="text">{ this.state.quoteText }</div>
          <div id="author">{ this.state.quoteAuthor }</div>
        </div>
        <button onClick={this.handleNewQuote} id="new-quote">Get New Quote</button>
        <a href={ this.state.tweetUrl } target="_blank" id="tweet-quote"><i class="fab fa-twitter"></i> Tweet Quote</a>
      </main>
    );
  }
}

//Printing to screen
ReactDOM.render(<App />, document.getElementById('root'));

Leave a Comment