Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

ReactJS Authentication Tutorial, Part 2

DZone's Guide to

ReactJS Authentication Tutorial, Part 2

In Part 2 of our series, we explore how to use ReactJS to create the front-end for our Chuck Norris Jokes web application.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Welcome back to our series on ReactJS! If you missed Part 1, you can find it here

Build The Front-End With ReactJS

In the early days of ReactJS, there was no tool or common way to set up a ReactJS app. However, React is more mature now; plenty of boilerplates, starters, and open-source tools are currently available to help you set up an app. There is one that stands out because of its simplicity. It's called the Create-React-App (CRA) CLI tool. It's being maintained by Facebook.

Note: We have a custom React script that comes bundled with Auth0 authentication. So you can use create-react-app to boostrap an app with authentication support like this create-react-app my-app --scripts-version auth0-react-scripts

Go ahead and install the CRA tool globally like so:

npm install -g create-react-app

After installing globally, go ahead and scaffold a new ReactJS app like so:

create-react-app chucknorrisworld

Then open http://localhost:3000 to see your app.

App recently scaffolded and showing at Localhost

Note:create-react-app automatically invokes Yarn for installation. If you don't have Yarn installed, it falls back to using npm.

Let's check out the structure of our newly scaffolded app.

Scaffolded App

my-app/
  README.md
  node_modules/ - All the packages required for the react app resides here
  package.json - File that contains the names of all the packages residing in node_modules folder
  public/
    index.html -  Index file that declares the root div where the App component is been bound to
    favicon.ico - The app’s favicon
  src/
    App.css - File that contains styles for the App component
    App.js - Basic App Component
    App.test.js - Test file that contains tests for the App Component
    index.css - File that contains style for root div
    index.js - Javascript file that binds the root div to the parent App Component
    logo.svg

We will work with this structure but make a few modifications. First, delete the App.test.js file.

Note: We are not writing any tests for this application. It's out of the scope of this tutorial. If you want to learn how to test your ReactJS applications, check out testing react applications with Jest.

Make the following modifications:

  • Create a folder called components inside the src directory. This will house our components.
  • Create a CelebrityJokes.js file inside the components directory. This component will take care of fetching the celebrity jokes and displaying them to the user.
  • Create a FoodJokes.js file inside the components directory. This component will take care of fetching the food jokes and displaying them to the user.
  • Create a Nav.js file inside the components directory. This component will be in charge of our navigation throughout the app.
  • Create a folder called utils inside the src directory. This will house our helper functions.
  • Delete the App.js file. Are you surprised? Don’t worry, we won’t need it.

Fetch the API Data

The first thing we need to do is to fetch the API data from our Node backend to display it in our app. Make sure the Node server is running.

Let's create a helper file to handle fetching the API. Create a chucknorris-api.js file inside the utils directory.

Open up the file and add code to it like so:

import axios from 'axios';

const BASE_URL = 'http://localhost:3333';

export {getFoodData, getCelebrityData};

function getFoodData() {
  const url = `${BASE_URL}/api/jokes/food`;
  return axios.get(url).then(response => response.data);
}

function getCelebrityData() {
  const url = `${BASE_URL}/api/jokes/celebrity`;
  return axios.get(url).then(response => response.data);
}

chucknorris-api.js

Note: Install axios in your app by running npm install axios --save.

We are using a very good promise based HTTP client, axios. An alternative for this is superagent.

In the getFoodData and getCelebrityData functions, axios fetches data from the API endpoints. Then we do this: export {getFoodData, getCelebrityData}; to make them ready for use in our components.

Build the Nav Component

The Nav.js file is our Nav component. Go ahead and add code to it like so:

import React, { Component } from 'react';
import { Link } from 'react-router';
import '../App.css';

class Nav extends Component {

  render() {
    return (
      <nav className="navbar navbar-default">
        <div className="navbar-header">
          <Link className="navbar-brand" to="/">Chuck Norris World</Link>
        </div>
        <ul className="nav navbar-nav">
          <li>
            <Link to="/">Food Jokes</Link>
          </li>
          <li>
           <Link to="/special">Celebrity Jokes</Link>
          </li>
        </ul>
        <ul className="nav navbar-nav navbar-right">
          <li><button className="btn btn-info log">Log In</button></li>
          <li><button className="btn btn-danger log">Log out </button></li>
        </ul>
      </nav>
    );
  }
}

export default Nav;

Note: Open up your terminal and install react-router like so: npm install react-router@3.0.0 --save (at the time of this writing, react-router is in 4.0 alpha, so you can explore its features).

The Link Component from react-router enables seamless client-side transition between routes without any page reload.

Build the Celebrityjokes and Foodjokes Component

By default, these two components will look similar in functionalities. They both display data from different endpoints. Let's start with the FoodJokescomponent.

import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';
import { getFoodData } from '../utils/chucknorris-api';


class FoodJokes extends Component {

  constructor() {
    super()
    this.state = { jokes: [] };
  }

  getFoodJokes() {
    getFoodData().then((jokes) => {
      this.setState({ jokes });
    });
  }

  componentDidMount() {
    this.getFoodJokes();
  }

  render() {

    const { jokes }  = this.state;

    return (
      <div>
        <Nav />
        <h3 className="text-center">Chuck Norris Food Jokes</h3>
        <hr/>

        { jokes.map((joke, index) => (
              <div className="col-sm-6" key={index}>
                <div className="panel panel-primary">
                  <div className="panel-heading">
                    <h3 className="panel-title"> <span className="btn">#{ joke.id }</span></h3>
                  </div>
                  <div className="panel-body">
                    <p> { joke.joke } </p>
                  </div>
                </div>
              </div>
          ))}

        <div className="col-sm-12">
          <div className="jumbotron text-center">
            <h2>Get Access to Celebrity Jokes By Logging In</h2>
          </div>
        </div>

        <div className="col-sm-12">
            <div className="jumbotron text-center">
              <h2>View Celebrity Jokes</h2>
              <Link className="btn btn-lg btn-success" to='/special'> Celebrity Jokes </Link>
            </div>
        </div>
      </div>
    );
  }
}

export default FoodJokes;

FoodJokes.js

Note: Learn why I use super() in the class constructor.

Let's analyze the code above. The FoodJoke component is pulling data from an API, so it needs a way of holding that data. That's where state comes in. In ReactJS, you can use props to pass data around and use state to hold/manage that data.

In the constructor, we define the initial state as seen in the code below:

...
 constructor() {
    super()
    this.state = { jokes: [] };
  }
...

In the getFoodJokes method, we call the getFoodData method we exported from the chucknorris-api.js helper file and set state as seen below:

...
 getFoodJokes() {
    getFoodData().then((jokes) => {
      this.setState({ jokes });
    });
  }
...

Now, we took advantage of one of the ReactJS lifecycle hooks, componentDidMount. Whatever is defined in this method is applied immediately after a component is mounted on the browser screen. So, we invoked the getFoodJokes method in the hook as seen below:

...
 componentDidMount() {
    this.getFoodJokes();
  }
...

All we are trying to do is tell ReactJS to load the data from the API immediately when the FoodJokes component gets rendered.

Finally, we rendered the component with the ReactJSrender method. This is the method that does the actual rendering on the screen. As seen in the code below, we extracted the loaded jokes from the state into a jokes constant.

We looped through the jokes constant which is now an array to display the contents on the screen.

Note: In ReactJS, when you loop through some form of data, you have to provide the key property and make sure it has a unique value, else an error will be thrown!

...
 const { jokes }  = this.state;
...

{ jokes.map((joke, index) => (
            <div className="col-sm-6" key={index}>
              <div className="panel panel-primary">
                <div className="panel-heading">
                  <h3 className="panel-title"> <span className="btn">#{ joke.id }</span></h3>
                </div>
                <div className="panel-body">
                  <p> { joke.joke } </p>
                </div>
              </div>
            </div>
))}

.....

Now, let's build the CelebrityJokes component in the same way:

import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';
import { getCelebrityData } from '../utils/chucknorris-api';

class CelebrityJokes extends Component {

  constructor() {
    super();
    this.state = { jokes: [] };
  }

  getCelebrityJokes() {
    getCelebrityData().then((jokes) => {
      this.setState({ jokes });
    });
  }

  componentDidMount() {
    this.getCelebrityJokes();
  }

  render() {

    const { jokes } = this.state;

    return (
      <div>
        <Nav />
        <h3 className="text-center">Privileged Chuck Norris Celebrity Jokes</h3>
        <hr/>

        { jokes.map((joke, index) => (
              <div className="col-sm-6" key={index}>
                <div className="panel panel-danger">
                  <div className="panel-heading">
                    <h3 className="panel-title"><span className="btn">#{ joke.id }</span></h3>
                  </div>
                  <div className="panel-body">
                    <p> { joke.joke } </p>
                  </div>
                </div>
              </div>
          ))}

        <div className="col-sm-12">
          <div className="jumbotron text-center">
            <h2>View Food Jokes</h2>
            <Link className="btn btn-lg btn-success" to='/'>Chuck Norris Food Jokes </Link>
          </div>
        </div>
      </div>
    );
  }
}

export default CelebrityJokes;

CelebrityJokes.js

Grab your coffee at this point because you have successfully created the Nav, CelebrityJokes, and FoodJokes components. Whoop! Whoop!

We need to take care of one more component so that our app can function. Can you guess? Yes, the root component!

Build the Root Component

This is the component where we get to define how routing should work in our application and also bind it to the root div that holds the whole app.

Open up index.js and add code to it like so:

import React from 'react';
import ReactDOM from 'react-dom';
import CelebrityJokes from './components/CelebrityJokes';
import FoodJokes from './components/FoodJokes';
import { Router, Route, browserHistory } from 'react-router';

const Root = () => {
  return (
    <div className="container">
      <Router history={browserHistory}>
        <Route path="/" component={FoodJokes}/>
        <Route path="/special" component={CelebrityJokes}/>
      </Router>
    </div>
  )
}

ReactDOM.render(<Root />, document.getElementById('root'));

index.js

You might quickly notice that we are not defining a class here, rather we just defined a Root function. ReactJS allows you to do that. Then, we imported the Router from react-router.

...
<Router history={browserHistory}>
  <Route path="/" component={FoodJokes}/>
  <Route path="/special" component={CelebrityJokes}/>
</Router>
...

The routing is simple. We have defined it to display the FoodJokes component once a user hits the / route. It displays the CelebrityJokes component once a user hits the /special route. The Beginner's Guide to React Router will give you a better understanding of how routing works in ReactJS.

This ReactDOM.render(<Root />, document.getElementById('root'));renders the root component in the root div, which is the starting point of our ReactJS application.

We imported all the required components like so:

import React from 'react';
import ReactDOM from 'react-dom';
import CelebrityJokes from './components/CelebrityJokes';
import FoodJokes from './components/FoodJokes';
import { Router, Route, browserHistory } from 'react-router';

Just a few things before we check our application in the browser:

  • Open up public/index.html and add bootstrap. Now the content of the HTML file should look like this:
<!doctype html>
  <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
      <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
      <!--
        Notice the use of %PUBLIC_URL% in the tag above.
        It will be replaced with the URL of the `public` folder during the build.
        Only files inside the `public` folder can be referenced from the HTML.

        Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
        work correctly both with client-side routing and a non-root public URL.
        Learn how to configure a non-root public URL by running `npm run build`.
      -->
      <title>React App</title>
    </head>
    <body>
      <div id="root"></div>
      <!--
        This HTML file is a template.
        If you open it directly in the browser, you will see an empty page.

        You can add webfonts, meta tags, or analytics to this file.
        The build step will place the bundled scripts into the <body> tag.

        To begin the development, run `npm start`.
        To create a production bundle, use `npm run build`.
      -->
    </body>
  </html>
  • Open up App.css and add this style like so:
.navbar-right { margin-right: 0px !important}
.log {
  margin: 5px 10px 0 0;
}

Feel free to check out your application in the browser. Right now, you should have something like this:

HomepageHomepage

CelebritypageCelebrityPage

Chuck Norris World DemoCurrent Application

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
web dev ,cli ,reactjs ,front-end

Published at DZone with permission of Prosper Otemuyiwa, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}