FCC Zipline Series 102: Build a Random Quote Machine | The React Way

This time, we are going to be building a Random Quote Machine. We must code a page that replicates the functionalities present here. The user stories that we must fulfil are the following:

  • As a user, I can click a button to show me a new random quote.
  • Bonus: As a user, I can press a button to tweet out a quote.

I’m going to take this chance to let you take a peek into React. If you just want the walk-through for the vanilla JavaScript version, take a look at this other post.

You may, or may not have heard about React before, but it’s getting quite popular as of late. It’s a library made by the guys at Facebook, who describe it as the following:

React: A JavaScript library for building user interfaces.

It’s a front-end framework similar to Angular, Ember or Backbone (someone reading this is probably orchestrating my assassination right now, bear with me please) but not quite so. In a MVC (Model View Controller) architecture, we could say that react is our V (View).

Hopefully, this exercise will grant you some insight into what React is and how it operates at a basic level; and help you decide whether to give it a shot or not. I’m also putting up an AngularJS and vanilla JavaScript version of this same exercise, in case you’re more interested in those.

React comes off as this scary thing that likes using strange XML syntax inside our JavaScript code. This syntax, known as JSX, is a gift from the gods. In React, we create components for our application inside the JS code, and then render them out using ReactDOM.

Let’s see a quick example, I’m going to set up our index.html file for the Random Quote Machine. I’m going to include the stylesheet and three scripts that React needs to properly work (namely, React, React DOM and Babel‘s browser.js, this last one transpiles JSX into plain JS in the browser). I’m also going to create a single div and give it an ID:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Random Quote Machine</title>

    <link rel="stylesheet" href="css/main.css"/> <!-- CSS file -->
</head>
<body>
    
<!-- Magical unicorn DIV element--> http://bower_components/babel/browser.min.js <!-- Babel --> http://bower_components/react/react.js <!-- React --> http://bower_components/react/react-dom.min.js <!-- React DOM--> </body> </html>

 

Now, I’m also going to add in our JS file, but I’m going to set it’s type to “text/babel”, so that the Babel transpiler knows that it isn’t just JavaScript, and it needs to do it’s magic; This is what the line looks like:


 

I’m going to place that like right before the closing tag:



    
    Random Quote Machine

    


    
http://bower_components/babel/browser.min.js http://bower_components/react/react.js http://bower_components/react/react-dom.min.js <script type="text/babel" src="js/app.js"> <!-- JS file --> </body> </html>

 

And now, let’s get to the cool stuff. We are going to start by creating our first React component and rendering it to our page:

var RandomQuoteApp = React.createClass({
  render: function() {
    return (
      <p>Hello World!</p>
    );
  }
});

ReactDOM.render(<RandomQuoteApp />, document.getElementById('random-quote-machine'));

 

That’s a good first step, as you can see, React.createClass() will create a new React component. We get the actual markup from within the render property. In this method, we return this JSX magic weird XML looking stuff. Then, ReactDOM.render() will append our newly created element to element that we give it as second argument (in this case, the

that we created in the index.html file).

That’s pretty nice and all, we just created a paragraph element containing Hello World in 86 characters! Let’s get something done though. In React, we need to think in COMPONENTS. We must dissect everything into little pieces that have a very specific task to them.

Imagine that this is what we want to end up with:

random-quote-machine

 

Let’s dissect it into components:

random-quote-machine-components

 

Perfect! Now we know what components we should be creating, let’s go one by one, starting with the RandomQuoteApp that we already set up, let’s change it to this:

var RandomQuoteMachine = React.createClass({
  render: function() {
    return (
      
Get random quote!
); } });

 

You may notice how I’m using className inside the component markup; It’s exactly the same as using class in HTML!

We now have a component that has some other components inside. We haven’t created them though, let’s fix that and get the QuoteContainer set up:

var QuoteContainer = React.createClass({
  render: function() {
      return (
          

Quote text will go here!

Author goes here!

); } });

 

Simple enough, we are merely creating a couple divs and giving them classes. An important point to have in mind is that every React component can only have ONE root element. In other words, this works:

var QuoteContainer = React.createClass({
  render: function() {
      return (
          

Hello World!

I'm a React component!

); } });

 

And this does not:

var QuoteContainer = React.createClass({
  render: function() {
      return (
          <p>Hello World!</p>
          <p>I'm a React component!</p>
      );
  }
});

 

Let’s also take care of the tweet button. This one’s a bit tricky, I’m basically taking it from the official Twitter API site, a twitter button is something like this in plain HTML:

http://platform.twitter.com/widgets/tweet_button.html?count=horizontal&dnt=false&lang=en&size=m&text=TWEET_TEXT&url=TWEET_URL%20style=

 

Changing TWEET_TEXT and TWEET_URL will do the trick for you. If you don’t want the URL to show up in the tweet, set it to an empty string (“”). Since we are just getting started, I’ll leave it that way for the moment, here’s the component (notice that I changed class to className and how the styles attribute is represented like an object):

var TweetButton = React.createClass({
    render: function() {
        return (
            http://platform.twitter.com/widgets/tweet_button.html?count=horizontal&dnt=false&lang=en&size=m&text=TWEET_TEXT&url=TWEET_URL
        );
    }
});

 

Good! We got every component in there, now, how do we represent data? It’s pretty easy, we pass in whatever we need as attributes for that particular component, and then, we can access them from within it using the this.props object. Easier seen that explained, here’s the main component and QuoteContainer:

var RandomQuoteMachine = React.createClass({
    render: function() {
        return (
            
Get random quote!
); } }); var QuoteContainer = React.createClass({ render: function() { return (

{ this.props.quote}

{ this.props.author}

); } });

 

As you can see, we pass in the quote and author to the QuoteContainer as attributes, and then access them using { this.props.WHATEVER }.

Let’s do the same for the TwitterButton component:

var RandomQuoteMachine = React.createClass({
    render: function() {
        return (
            
Get random quote!
); } }); var TweetButton = React.createClass({ render: function() { return ( http://platform.twitter.com/widgets/tweet_button.html?count=horizontal&dnt=false&lang=en&size=m&text= ); } });

 

Here, I did the same thing, but put the brackets around the whole src attribute, and then used plain old JS encodeURI to turn spaces and special characters into valid URL characters. Anything between the curly braces can be considered JavaScript, so go as crazy as you want.

Now that we have working components, how about we give them random quotes rather than plain text? We are going to be working with the main RandomQuoteApp component from now on.

First, we’ll give it another method getRandomQuote and have it return a quote to us:

var RandomQuoteMachine = React.createClass({
    getRandomQuote: function() {
        var quotes = [
            {author: "Charles Bernstein", quote: "The combination of low culture and high technology is one of the most fascinating social features of the video game phenomenon. Computers were invented as super drones to do tasks no human in her or his right mind (much less left brain) would have the patience, or the perseverance, to manage. Now our robot drones, the ones designed to take all the boring jobs, become the instrument for libidinal extravaganzas devoid of any socially productive component. Video games are computers neutered of purpose, liberated from functionality. The idea is intoxicating; like playing with the help on their night off."},
            {author: "John D. Carmack", quote: "Story in a game is like a story in a porn movie. It's expected to be there, but it's not that important."},
            {author: "Barack Obama", quote: "It's tough to "buy American" when a video game sold by a U.S. company has been developed by Japanese software engineers and packaged in Mexico."},
            {author: "Heather Chaplin & Aaron Ruby", quote: "Show me your children's games, and I will show you the next hundred years."},
            {author: "Sid Meier", quote: "In a way, trying to impress people with design or personality or whatever works to promote movies doesn't work with games because it takes the focus off the player who is supposed to be the star. The more the player is the star, the better a game you have."},
            {author: 'Half Life 2', quote: "The right man in the wrong place can make all the difference in the world."},
            {author: "Bioshock", quote: "Is a man not entitled to the sweat of his brow?"},
            {author: "Duke Nukem", quote: "It's time to kick ass and chew bubble gum, and I'm all out of gum."},
            {author: "The Legend of Zelda", quote: "It’s dangerous to go alone; take this!"}
        ];

        var random = randomGen(0, quotes.length - 1);
        return quotes[random];

        function randomGen(max, min) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    },
    render: function() {
        return (
            
Get random quote!
); } });

 

There is nothing inherently new to it, I’m merely generating a random number based on the amount of quotes available and returning a quote from the quote array that I set up.

Now, we are going to set the state for our component. We could say that the state is an object that contains information relevant to the component at a given point in time. We set the initial state for a component using the getInitialState method:

var RandomQuoteMachine = React.createClass({
    getInitialState: function() {
      return {
        currentQuote: this.getRandomQuote()
      }
    },
    getRandomQuote: function() {
        var quotes = [
            {author: "Charles Bernstein", quote: "The combination of low culture and high technology is one of the most fascinating social features of the video game phenomenon. Computers were invented as super drones to do tasks no human in her or his right mind (much less left brain) would have the patience, or the perseverance, to manage. Now our robot drones, the ones designed to take all the boring jobs, become the instrument for libidinal extravaganzas devoid of any socially productive component. Video games are computers neutered of purpose, liberated from functionality. The idea is intoxicating; like playing with the help on their night off."},
            {author: "John D. Carmack", quote: "Story in a game is like a story in a porn movie. It's expected to be there, but it's not that important."},
            {author: "Barack Obama", quote: "It's tough to "buy American" when a video game sold by a U.S. company has been developed by Japanese software engineers and packaged in Mexico."},
            {author: "Heather Chaplin & Aaron Ruby", quote: "Show me your children's games, and I will show you the next hundred years."},
            {author: "Sid Meier", quote: "In a way, trying to impress people with design or personality or whatever works to promote movies doesn't work with games because it takes the focus off the player who is supposed to be the star. The more the player is the star, the better a game you have."},
            {author: 'Half Life 2', quote: "The right man in the wrong place can make all the difference in the world."},
            {author: "Bioshock", quote: "Is a man not entitled to the sweat of his brow?"},
            {author: "Duke Nukem", quote: "It's time to kick ass and chew bubble gum, and I'm all out of gum."},
            {author: "The Legend of Zelda", quote: "It’s dangerous to go alone; take this!"}
        ];

        var random = randomGen(0, quotes.length - 1);
        return quotes[random];

        function randomGen(max, min) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    },
    render: function() {
        return (
            
Get random quote!
); } });

 

There we go, note that getInitialState must always return an object (or null!). This “state” object can be accessed from anywhere within the component using the this.state object. Since we want to pass the current quote down to the QuoteContainer and TweetButton, let’s do it using the curly braces in the render method!

var RandomQuoteMachine = React.createClass({
    getInitialState: function() {
      return {
        currentQuote: this.getRandomQuote()
      }
    },
    getRandomQuote: function() {
        var quotes = [
            {author: "Charles Bernstein", quote: "The combination of low culture and high technology is one of the most fascinating social features of the video game phenomenon. Computers were invented as super drones to do tasks no human in her or his right mind (much less left brain) would have the patience, or the perseverance, to manage. Now our robot drones, the ones designed to take all the boring jobs, become the instrument for libidinal extravaganzas devoid of any socially productive component. Video games are computers neutered of purpose, liberated from functionality. The idea is intoxicating; like playing with the help on their night off."},
            {author: "John D. Carmack", quote: "Story in a game is like a story in a porn movie. It's expected to be there, but it's not that important."},
            {author: "Barack Obama", quote: "It's tough to "buy American" when a video game sold by a U.S. company has been developed by Japanese software engineers and packaged in Mexico."},
            {author: "Heather Chaplin & Aaron Ruby", quote: "Show me your children's games, and I will show you the next hundred years."},
            {author: "Sid Meier", quote: "In a way, trying to impress people with design or personality or whatever works to promote movies doesn't work with games because it takes the focus off the player who is supposed to be the star. The more the player is the star, the better a game you have."},
            {author: 'Half Life 2', quote: "The right man in the wrong place can make all the difference in the world."},
            {author: "Bioshock", quote: "Is a man not entitled to the sweat of his brow?"},
            {author: "Duke Nukem", quote: "It's time to kick ass and chew bubble gum, and I'm all out of gum."},
            {author: "The Legend of Zelda", quote: "It’s dangerous to go alone; take this!"}
        ];

        var random = randomGen(0, quotes.length - 1);
        return quotes[random];

        function randomGen(max, min) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    },
    render: function() {
        return (
            
Get random quote!
); } });

 

Awesome, our components should be displaying a random quote by now, but having to refresh every time that we want to get a new quote isn’t too convenient. Let’s have the “Get random quote!” button do something other than taking up space.

I’m going to create yet another method inside the RandomQuoteApp component and call it handleRandomQuote. In here, we are going to generate a new random quote and update the state. To update the state for a component, we can call the setState method:

var RandomQuoteMachine = React.createClass({
    getInitialState: function() {
      return {
        currentQuote: this.getRandomQuote()
      }
    },
    getRandomQuote: function() {
        var quotes = [
            {author: "Charles Bernstein", quote: "The combination of low culture and high technology is one of the most fascinating social features of the video game phenomenon. Computers were invented as super drones to do tasks no human in her or his right mind (much less left brain) would have the patience, or the perseverance, to manage. Now our robot drones, the ones designed to take all the boring jobs, become the instrument for libidinal extravaganzas devoid of any socially productive component. Video games are computers neutered of purpose, liberated from functionality. The idea is intoxicating; like playing with the help on their night off."},
            {author: "John D. Carmack", quote: "Story in a game is like a story in a porn movie. It's expected to be there, but it's not that important."},
            {author: "Barack Obama", quote: "It's tough to "buy American" when a video game sold by a U.S. company has been developed by Japanese software engineers and packaged in Mexico."},
            {author: "Heather Chaplin & Aaron Ruby", quote: "Show me your children's games, and I will show you the next hundred years."},
            {author: "Sid Meier", quote: "In a way, trying to impress people with design or personality or whatever works to promote movies doesn't work with games because it takes the focus off the player who is supposed to be the star. The more the player is the star, the better a game you have."},
            {author: 'Half Life 2', quote: "The right man in the wrong place can make all the difference in the world."},
            {author: "Bioshock", quote: "Is a man not entitled to the sweat of his brow?"},
            {author: "Duke Nukem", quote: "It's time to kick ass and chew bubble gum, and I'm all out of gum."},
            {author: "The Legend of Zelda", quote: "It’s dangerous to go alone; take this!"}
        ];

        var random = randomGen(0, quotes.length - 1);
        return quotes[random];

        function randomGen(max, min) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    },
    handleRandomQuote: function() {
      this.setState({
        currentQuote: this.getRandomQuote()
      });
    },
    render: function() {
        return (
            
Get random quote!
); } });

 

We’re almost there! Our new method will update the state when called. When the state changes, the QuoteContainer and TweetButton will get automatically updated with the new data!

But for that, we need to actually call the handleRandomQuote method when we click on the button. This is easily done using the onClick attribute:

var RandomQuoteMachine = React.createClass({
    getInitialState: function() {
      return {
        currentQuote: this.getRandomQuote()
      }
    },
    getRandomQuote: function() {
        var quotes = [
            {author: "Charles Bernstein", quote: "The combination of low culture and high technology is one of the most fascinating social features of the video game phenomenon. Computers were invented as super drones to do tasks no human in her or his right mind (much less left brain) would have the patience, or the perseverance, to manage. Now our robot drones, the ones designed to take all the boring jobs, become the instrument for libidinal extravaganzas devoid of any socially productive component. Video games are computers neutered of purpose, liberated from functionality. The idea is intoxicating; like playing with the help on their night off."},
            {author: "John D. Carmack", quote: "Story in a game is like a story in a porn movie. It's expected to be there, but it's not that important."},
            {author: "Barack Obama", quote: "It's tough to "buy American" when a video game sold by a U.S. company has been developed by Japanese software engineers and packaged in Mexico."},
            {author: "Heather Chaplin & Aaron Ruby", quote: "Show me your children's games, and I will show you the next hundred years."},
            {author: "Sid Meier", quote: "In a way, trying to impress people with design or personality or whatever works to promote movies doesn't work with games because it takes the focus off the player who is supposed to be the star. The more the player is the star, the better a game you have."},
            {author: 'Half Life 2', quote: "The right man in the wrong place can make all the difference in the world."},
            {author: "Bioshock", quote: "Is a man not entitled to the sweat of his brow?"},
            {author: "Duke Nukem", quote: "It's time to kick ass and chew bubble gum, and I'm all out of gum."},
            {author: "The Legend of Zelda", quote: "It’s dangerous to go alone; take this!"}
        ];

        var random = randomGen(0, quotes.length - 1);
        return quotes[random];

        function randomGen(max, min) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    },
    handleRandomQuote: function() {
      this.setState({
        currentQuote: this.getRandomQuote()
      });
    },
    render: function() {
        return (
            
Get random quote!
); } });

 

And that’s it! For a bonus, get some styles in place! Here’s my SCSS file:

@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700);

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
}

.main-container {
    width: 80%;
    margin: 20px auto 0 auto;
    max-width: 960px;
}

.quote-container {
    padding: 20px;
    border: 1px solid #555;
    min-height: 150px;
    
    p.quote-text {
        margin-bottom: 20px;
    }
    
    p.said-by {
        font-weight: 700;
        text-align: right;
    }
}

button.random-quote {
    position: relative;
    top: -1px;
    width: 100%;
    height: 30px;
    background-color: transparent;
    border: 1px solid #555;
    cursor: pointer;
    font-weight: 700;
    transition: 0.2s ease-out all;
    
    &:hover {
        color: #E4460C;
        border-color: #E4460C;
    }
}

.twitter-share-button {
    margin-top: 10px;
}

 

You can check out the live version here, along with an AngularJS and vanilla JavaScript version. As always, feel free to shoot me an email, ping me on twitter or post a comment below!