How To Create Login Page Using React Js
This article was originally published on Stormpath. Thank you for supporting the partners who make SitePoint possible.
React (sometimes referred to as React.js) is an awesome way to build web UIs. The Stormpath React SDK extends React and React Router with routes and components that allow you to solve common user management tasks using Stormpath, such as authentication and authorization.
Lately, React has picked up quite some attention, and it's easy to understand why. React allows you to turn complex UIs into simple and reusable components that can be composed easily together.
This post will show you how to build a React application from scratch, using the Stormpath React SDK to add features that allow people to sign up, log in, and even view their own user profile.
Let's get started!
The React + Express.js Application Stack
Since we're building our app from scratch, we'll use ES6 and JSX to write as little code as possible, as well as the Stormpath React SDK for user features.
To give you a good overview of what we'll be using:
- React – Allows us to compose simple yet powerful UIs.
- ReactRouter – Organizes the URL navigation in our React application.
- ES6 – The next version of JavaScript. Allows us to write real JavaScript classes.
- JSX – Allows us to place HTML in JavaScript without concatenating strings.
- Stormpath – Allows us to store and authenticate users without having to create our own backend for it.
- Stormpath React SDK – Integrates registration forms, login pages and authentication into our React application with very little effort.
- Express – Allows us to serve our HTML and JavaScript files.
- Express Stormpath – Allows us to serve Stormpath's API through Express.
- Webpack – Allows us to pack all of our JavaScript files into one bundle.
- Babel – Allows us to transpile our ES6 and JSX into ES5.
- Bootstrap – Because we want things to be pretty.
Setting up Our React + Express.js Project
Start by creating a new project directory and a package.json
file for it.
$ mkdir my-react-app $ cd my-react-app $ npm init --yes
Now install Express, the Stormpath module for Express, and Body Parser:
$ npm install --save express express-stormpath body-parser
We need a server to host our application, so create a new file named server.js
and put the code below in it:
var express = require('express'); var stormpath = require('express-stormpath'); var bodyParser = require('body-parser'); var app = express(); app.use(stormpath.init(app, { web: { produces: ['application/json'] } })); app.on('stormpath.ready', function () { app.listen(3000, 'localhost', function (err) { if (err) { return console.error(err); } console.log('Listening at http://localhost:3000'); }); });
Awesome. Now we can hook that up to a Stormpath Application by creating a new file named stormpath.yml
with the following code in it. And yeah, you do have to replace those values in it with your own.
client: apiKey: id: YOUR_API_KEY_ID secret: YOUR_API_KEY_SECRET application: href: https://api.stormpath.com/v1/applications/XXXX <-- YOUR APP HREF
So far so good. Now try the server by running $ node server.js
. If everything is set up correctly then you should see:
Listening at http://localhost:3000
If you saw that message, you've successfully configured your server to talk with Stormpath and expose a REST API for our React application to use.
Configuring Webpack
Before you get too excited, kill the server and install Webpack so that we can package all of our client-side scripts (we'll need this organization soon).
$ npm install --save webpack $ npm install --save-dev webpack-dev-middleware
Configure Webpack by creating a new file named webpack.config.js
and put the code below in it:
var path = require('path'); var webpack = require('webpack'); module.exports = { entry: [ './src/app' ], devtool: 'eval-source-map', output: { path: __dirname, filename: 'app.js', publicPath: '/js/' }, module: { loaders: [] } };
What this will do is look in our /src/
directory (that we'll create shortly) and package all of the scripts and their dependencies under that directory as one module. Then use the file /src/app.js
and its exports as the export of that module. Then finally when it has generated that module package, it will serve that through Express under the /js/app.js
endpoint.
But in order for Express to serve Webpack files, we have to open up server.js
and add these lines to the top of it:
var webpack = require('webpack'); var config = require('./webpack.config');
Then immediately after the line var app = express();
add:
var compiler = webpack(config); app.use(require('webpack-dev-middleware')(compiler, { noInfo: true, publicPath: config.output.publicPath }));
As I mentioned before, this will allow Webpack to intercept requests and serve our packaged /js/app.js
file.
Configuring Babel
Since we'll be using ES6 and JSX, we need to transpile these files into ES5 (for backwards compatibility with non-modern browsers). This is where Babel comes in. Babel can take our ES6/JSX files as input, and convert those to ES5.
To use Babel, start by installing some dependencies:
$ npm install --save babel-core babel-runtime babel-loader babel-plugin-react-transform \ babel-preset-es2015 babel-preset-react babel-preset-stage-0
Now we'll instruct Babel on how to compile our files, so create a new file named `.babelrc` and add this code it:
{ "presets": ["stage-0", "es2015", "react"] }
Finally, in order to get Babel to work with Webpack, we need to edit `webpack.config.js` and add an entry to the `module.loaders` array, as shown below:
module: { loaders: [{ test: /\.js$/, loaders: ['babel'], include: path.join(__dirname, 'src') }] }
Index.html and Bootstrap
Now, before getting our hands dirty with React, we'll prepare the entry page for our app. This page will tell the browser what it must load before we initialize React and our application. So create a new directory named build
, then within that, put a file named index.html
. Our server will serve all of our static files from this folder.
$ mkdir build $ cd build $ touch index.html
Then within index.html
, put the following:
<!doctype html> <!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7"><![endif]--> <!--[if IE 7]><html class="no-js lt-ie9 lt-ie8"><![endif]--> <!--[if IE 8]><html class="no-js lt-ie9"><![endif]--> <!--[if gt IE 8]><!--> <html class="no-js"><!--<![endif]--> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <base href="/"> <title></title> <meta name="description" content=""> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="/css/bootstrap.min.css" /> </head> <body> <div id="app-container"></div> <script src="/js/app.js"></script> </body> </html>
Also, under the build
directory, create a new directory named css
and download Bootstrap to it. Name the file bootstrap.min.css
.
$ mkdir css $ cd css $ curl -O https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css $ cd ../.. # return to /my-react-app
Now in order for our browser to be able to access these files we need to configure them so that they are served through Express. So open up server.js
and at the top of the file add:
var path = require('path');
Then under the line app.use(stormpath.init(app, ...));
add:
app.get('/css/bootstrap.min.css', function (req, res) { res.sendFile(path.join(__dirname, 'build/css/bootstrap.min.css')); }); app.get('*', function (req, res) { res.sendFile(path.join(__dirname, 'build/index.html')); });
How Does React Work?
Now that we have the skeleton for our app done, we can focus on building our React app. But before we write any code, let's take a look at what React is and what it does for us.
Components
In React, everything is built upon components. You can think of a component as something that renders a DOM node. A simple React component looks like this:
class HelloWorld extends React.Component { render() { return <span>Hello World!</span>; } }
That was simple. Now, if you wanted to render this component to a page, then all you'd have to do is import React and then call:
ReactDOM.render( <HelloWorld />, document.getElementById('hello-world-element') );
And React would render the component to that element.
There are, of course, more things to a React component, such as state. Below is an example of a counter component that starts counting when added to the DOM and stops when removed.
class Counter extends React.Component { state = { current: 0 } constructor() { super(arguments...); this.intervalId = null; } updateCounter() { this.setState({ counter: this.state.current + 1 }); } componentWillMount() { this.setState({ counter: this.props.from || 0 }); this.intervalId = setInterval(this.updateCounter.bind(this), 1000); } componentWillUnmount() { clearInterval(this.intervalId); } render() { return <span>{ this.state.current }</span>; } }
Notice the methods componentWillMount()
and componentWillUnmount()
. These are component life-cycle methods that will be executed at various points of a component's life-cycle (in this case, mount and unmount). These methods are usually used for setting up and tearing down a component and is necessary to use because React will error if you try to set the state of a component when it hasn't been mounted yet.
Also notice this.props.from
. The member this.props
is a collection of all the properties (inputs) passed to a component. Properties of a component can be set as shown below:
<Counter from="50" /> <Counter from={ myVariable } />
JSX Variables
Variables can easily be interpolated into your JSX DOM using { nameOfVariable }
, e.g. as shown below:
render() { var myVariable = 123; return <span>{ myVariable }</span>; }
JSX and Reserved JavaScript Identifiers
Since JSX is JavaScript, there are some caveats that you need to know when working with React. I.e. when setting properties of a React DOM component you cannot use neither for
or class
since those are considered reserved JavaScript identifiers. To get around this problem, React has come up with htmlFor
and className
that you should use instead.
To illustrate the issue, this won't work:
<label for="my-input" class="my-label">My Input</label>
But this will:
<label htmlFor="my-input" className="my-label">My Input</label>
Virtual DOM
Instead of working directly against the DOM, in React all components are kept in their own virtual DOM. You can think of the virtual DOM as a DOM implementation in JavaScript (because it actually is). This virtual DOM is then mapped to a real DOM element. So when you render your React component, React will look at the DOM output from the component, compare it to its representation in the virtual DOM, and then generate a patch for the real DOM.
What this means is that you never have to think of manually manipulating DOM elements again. All you have to do is tell React how you want your component to look like, and it will take care of transforming the DOM the ways necessary (with minimal effort).
Installing React Dependencies
Now when we are acquainted with React, we'll kick things off by installing some React dependencies:
$ npm install --save react react-dom react-router react-stormpath react-document-title history
Before we start coding, we need a place to put our React files, so create a new directory named src
, and then use that as your working directory.
$ mkdir src $ cd src
Now, let's start with the entry point of our app. This will be the place where we will set up our React application and its routing. So create a new file named app.js
and enter this code:
import React from 'react'; import ReactDOM from 'react-dom'; import { Router, IndexRoute, Route, browserHistory } from 'react-router'; ReactDOM.render( <Router history={browserHistory}> </Router>, document.getElementById('app-container') );
So now we have a foundation for our application. Let's go ahead and import the Stormpath SDK and some things we'll need in it. At the top of your app.js
file, add the import statement:
import ReactStormpath, { Router, HomeRoute, LoginRoute, AuthenticatedRoute } from 'react-stormpath';
As you can see in app.js
there's now two conflicting Router
imports. Since ReactStormpath.Router
extends from ReactRouter.Router
we won't be needing that anymore. So go ahead and remove the Router
import from react-router
. Important: Leave the other ReactRouter imports, we'll be needing those later.
Now, we'll initialize the Stormpath SDK. Add the following line right above ReactDOM.render()
.
ReactStormpath.init();
That was easy! We're now ready to start building our pages.
Master Page
Before we create our pages, we have to set up our router. The router is what determines how we'll be able to navigate around in our React application. We'll start by creating a shared root route. This will act as our "master page". I.e. all routes under this route will all share the same master component (header). So place the code below inside the <Router>
tag in app.js
so that it looks like this:
<Router history={browserHistory}> <Route path='/' component={MasterPage}> </Route> </Router>
As you can see, we have referenced MasterPage
. Something that doesn't exist yet. So let's go ahead and create that in a new directory that we'll name pages
, in our src
folder.
$ mkdir pages $ cd pages
Now create a new file named MasterPage.js
and add this code to it:
import React from 'react'; import { Link } from 'react-router'; import { LoginLink } from 'react-stormpath'; import DocumentTitle from 'react-document-title'; import Header from './Header'; export default class is extends React.Component { render() { return ( <DocumentTitle title='My React App'> <div className='MasterPage'> <Header /> { this.props.children } </div> </DocumentTitle> ); } }
As you can see, we don't have a Header
component yet, so let's go and create a new file named Header.js
in the same directory with the following content:
import React from 'react'; import { Link } from 'react-router'; import { LoginLink, LogoutLink, Authenticated, NotAuthenticated } from 'react-stormpath'; export default class Header extends React.Component { render() { return ( <nav className="navbar navbar-default navbar-static-top"> <div className="container"> <div id="navbar-collapse" className="collapse navbar-collapse"> <ul className="nav navbar-nav"> <li><Link to="/">Home</Link></li> </ul> <ul className="nav navbar-nav navbar-right"> </ul> </div> </div> </nav> ); } }
Index Page
In our MasterPage
notice the property this.props.children
. This will contain the components of the child routes that our router match. So if we had a route that looked like:
<Route path='/' component={MasterPage}> <Route path='/hello' component={HelloPage} /> </Route>
And we tried to access /hello
. The this.props.children
array would be populated with a HelloPage
component and for that reason that component would be rendered in our master page.
Now imagine the scenario where you try to access /
. Without any this.props.children
, this would only render your master page but with empty content. This is where IndexRoute
comes into play. With an IndexRoute
you can specify the component that should be rendered when you hit the path of the master page route (in our case `/`).
But before we add our IndexRoute
to our router, let's create a new file in our pages
directory named IndexPage.js
and add the following to it:
import { Link } from 'react-router'; import React, { PropTypes } from 'react'; import { LoginLink } from 'react-stormpath'; export default class IndexPage extends React.Component { render() { return ( <div className="container"> <h2 className="text-center">Welcome!</h2> <hr /> <div className="jumbotron"> <p> <strong>To my React application!</strong> </p> <p>Ready to begin? Try these Stormpath features that are included in this example:</p> <ol className="lead"> <li><Link to="/register">Registration</Link></li> <li><LoginLink /></li> <li><Link to="/profile">Custom Profile Data</Link></li> </ol> </div> </div> ); } }
Now let's add our IndexRoute
. Open up app.js
and inside the tag <Route path='/' component={MasterPage}>
add your IndexRoute
so that it looks like the following:
<Route path='/' component={MasterPage}> <IndexRoute component={IndexPage} /> </Route>
Login Page
We now have a React application that shows a header with a default page. But we don't have a login page yet. So let's create a new file named `LoginPage.js` and add some content to it:
import React from 'react'; import DocumentTitle from 'react-document-title'; import { LoginForm } from 'react-stormpath'; export default class LoginPage extends React.Component { render() { return ( <DocumentTitle title={`Login`}> <div className="container"> <div className="row"> <div className="col-xs-12"> <h3>Login</h3> <hr /> </div> </div> <LoginForm /> </div> </DocumentTitle> ); } }
Notice the LoginForm
component. This is all we have to add in order for us to have a fully working form in which people can sign up from.
But before we can use it, we need to open up app.js
and add a route for the page in our router. So inside the tag <Route path='/' component={MasterPage}>
add the following:
<LoginRoute path='/login' component={LoginPage} />
In order to be able to access the login page, we need to add this to our menu. So go ahead and open up Header.js
and inside the element <ul className="nav navbar-nav navbar-right">
add the following:
<NotAuthenticated> <li> <LoginLink /> </li> </NotAuthenticated>
As you can see we're using the NotAuthenticated
component. With this we'll only show a LoginLink
when the user isn't logged in yet.
Registration Page
Now, let's add a page where people can sign up. We'll call it RegistrationPage
. So create a new file named RegistrationPage.js
and put the following content in it:
import React from 'react'; import DocumentTitle from 'react-document-title'; import { RegistrationForm } from 'react-stormpath'; export default class RegistrationPage extends React.Component { render() { return ( <DocumentTitle title={`Registration`}> <div className="container"> <div className="row"> <div className="col-xs-12"> <h3>Registration</h3> <hr /> </div> </div> <RegistrationForm /> </div> </DocumentTitle> ); } }
Notice that we used the RegistrationForm
component. As you might have guessed, this will render a Stormpath registration form. And once you've signed up it will point users to the login page where they'll be able to login.
In order to access this page. We need to add a route. So go ahead and open up app.js
and inside the tag <Route path='/' component={MasterPage}>
add:
<Route path='/register' component={RegistrationPage} />
We now have a route, but people won't be able to find the page unless we link to it, so open up Header.js
and add the following right before the closing tag (</ul>
) of <ul className="nav navbar-nav navbar-right">
:
<NotAuthenticated> <li> <Link to="/register">Create Account</Link> </li> </NotAuthenticated>
Notice the use of the NotAuthenticated
component. With this we'll only show the /register
link when the user isn't logged in.
Profile Page
Once a user is logged in, we want to be able to show them some personalized content (their user data). So create a new file named ProfilePage.js
and put the following code in it:
import React from 'react'; import DocumentTitle from 'react-document-title'; import { UserProfileForm } from 'react-stormpath'; export default class ProfilePage extends React.Component { render() { return ( <DocumentTitle title={`My Profile`}> <div className="container"> <div className="row"> <div className="col-xs-12"> <h3>My Profile</h3> <hr /> </div> </div> <div className="row"> <div className="col-xs-12"> <UserProfileForm /> </div> </div> </div> </DocumentTitle> ); } }
Notice that we use the UserProfileForm
. This is a simple helper form that allows you to edit the most basic user fields.
Though, in order to actually modify the user profile, we need to change a few things in our server. So open up server.js
and add the following route underneath app.use(stormpath.init(app, ...));
:
app.post('/me', bodyParser.json(), stormpath.loginRequired, function (req, res) { function writeError(message) { res.status(400); res.json({ message: message, status: 400 }); res.end(); } function saveAccount () { req.user.givenName = req.body.givenName; req.user.surname = req.body.surname; req.user.email = req.body.email; req.user.save(function (err) { if (err) { return writeError(err.userMessage || err.message); } res.end(); }); } if (req.body.password) { var application = req.app.get('stormpathApplication'); application.authenticateAccount({ username: req.user.username, password: req.body.existingPassword }, function (err) { if (err) { return writeError('The existing password that you entered was incorrect.'); } req.user.password = req.body.password; saveAccount(); }); } else { saveAccount(); } });
This will allow the form to change both the given name, surname, email and password of user.
If you have additional fields that you wish to edit, then simply customize the UserProfileForm
form and add the fields that you wish to edit in the route above.
Now, in order for us to access this page from the menu, open up Header.js
and right below <li><Link to="/">Home</Link></li>
add:
<Authenticated> <li> <Link to="/profile">Profile</Link> </li> </Authenticated>
With this, using the Authenticated
component, when we have a user session we'll render a link to the /profile
page and allow our users to view their user profile.
In order for us to be able to access the page, we must as with the other pages add it to the router. Open up app.js
and inside the tag <Route path='/' component={MasterPage}>
add:
<AuthenticatedRoute path='/profile' component={ProfilePage} />
Notice that we're using AuthenticatedRoute
. This is a route that can only be accessed if there is an authenticated user session. If there's no session, then the user will automatically be redirected to the path of the LoginLink
.
Home Route
Now when we've setup most of our routing. Let's look at a special route called the HomeRoute
. This route itself doesn't do anything. But acts as a "marker", to indicate where to redirect to when logging in and logging out.
So in order to specify where we want to end up when we log out, open up app.js
and change the:
<Route path='/' component={MasterPage}> ... </Route>
Into:
<HomeRoute path='/' component={MasterPage}> ... </HomeRoute>
Now when logging out, the Stormpath SDK will know that it should redirect to the '/' path. Now, to specify where to redirect when logging out, change the AuthenticatedRoute
that we created in the previous step:
<AuthenticatedRoute path='/profile' component={ProfilePage} />
So that it looks like:
<AuthenticatedRoute> <HomeRoute path='/profile' component={ProfilePage} /> </AuthenticatedRoute>
Notice how the AuthenticatedRoute
wraps the HomeRoute
. This is used to indicate the authenticated route that we want to redirect to after login.
Logout
Finally, once our users have signed up and logged in. We want to give them the option to logout. Fortunately, adding this is really simple.
So open up Header.js
and inside <ul className="nav navbar-nav navbar-right">
add this code to the end:
<Authenticated> <li> <LogoutLink /> </li> </Authenticated>
Notice the LogoutLink
component. Once this is clicked, the user session will be automatically destroyed and the user will be redirected to the unauthenticated HomeRoute
.
User State in Components
Access user state in your components by requesting the authenticated and user context types:
class ContextExample extends React.Component { static contextTypes = { authenticated: React.PropTypes.bool, user: React.PropTypes.object }; render() { if (!this.context.authenticated) { return ( <div> You need to <LoginLink />. </div> ); } return ( <div> Welcome {this.context.user.username}! </div> ); } }
Importing Components
To be able to reference our pages we need to import them. And in order to make importing easy, we'll put them all together in an index.js
file so we only have to import it once. So let's create a new file named index.js
in our pages
directory and export all of our pages from it, as shown below:
export MasterPage from './MasterPage' export IndexPage from './IndexPage' export LoginPage from './LoginPage' export RegistrationPage from './RegistrationPage' export ProfilePage from './ProfilePage'
With this, we'll only have to do one import in order to have access to all of our pages.
So let's do that. Open up app.js
file and at the top of the file, add the following import statement:
import { MasterPage, IndexPage, LoginPage, RegistrationPage, ProfilePage } from './pages';
Running The Project
Now we have an application where our users can sign up, login, and show their user data. So let's try it out!
As before, start our server by running the following:
$ node server.js
And if everything is running successfully you should be able to see this message:
Listening at http://localhost:3000
So, open up http://localhost:3000 in your browser and try it out!
Summary
As you have seen in this article, React is a really powerful tool and when used together with ES6, JSX, and Stormpath, building apps suddenly becomes fun again.
If there are any parts that you feel unsure of, feel free to check out the example project and use that as a reference implementation. I also enjoyed this post on React's design – it explains in beautiful detail why React is awesome.
And if you have questions regarding the Stormpath React SDK, be sure to check out its API documentation.
How To Create Login Page Using React Js
Source: https://www.sitepoint.com/tutorial-build-a-react-js-application-with-user-login-and-authentication/
Posted by: hubbardandome.blogspot.com
0 Response to "How To Create Login Page Using React Js"
Post a Comment