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.
Join the DZone community and get the full member experience.
Join For FreeWelcome 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.
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.
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 thesrc
directory. This will house our components. - Create a
CelebrityJokes.js
file inside thecomponents
directory. This component will take care of fetching the celebrity jokes and displaying them to the user. - Create a
FoodJokes.js
file inside thecomponents
directory. This component will take care of fetching the food jokes and displaying them to the user. - Create a
Nav.js
file inside thecomponents
directory. This component will be in charge of our navigation throughout the app. - Create a folder called
utils
inside thesrc
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 FoodJokes
component.
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:
Homepage
CelebrityPage
Current Application
If you enjoyed this article and want to learn more about React, check out this collection of tutorials and articles on all things React.
Published at DZone with permission of Prosper Otemuyiwa, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments