Continue the react-router tutorial in the article “Complete Tutorial React Router Dom For Beginners (Easy-To-Understand)“. We will create a simple login page and implement the protected route reactjs component to check whether the user is logged successfully and gained access to the dashboard page.
This tutorial contains a simple example where the user fill the input box (username and password) and identified manually (without API and JWT tokens). Then checks if the user and password match, the user will be automatically transferred on the dashboard page.
Vice versa, if the user accesses the dashboard page without going through the login process, the react router will redirect the user to the login page.
The purpose of this tutorial is to show a basic overview or process of using protect private routes in reactJS. So that, there is no use of redux or context in its creation. To implement react context or redux to make route authentication, we will discuss in another article.
Let’s get started!
How to create a simple login and protected route reactjs
1. Creating a new react app project
1 | npx create-react-app example-auth-login |
2. Install the react router component
The main library for route authentication is react-router-dom. Go to the root folder of the project that was created and run the following command.
1 | npm install react-router-dom |
3. Creating a Login Page Component
Create the Login.jsx file in the “/example-auth-login/src” folder. Fill the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import React, { Component } from "react"; import "./Login.css"; import { Redirect } from "react-router-dom"; class Login extends Component { constructor(props) { super(props); this.state = { islogged: false, loginParams: { user_id: "", user_password: "" } }; } handleFormChange = event => { let loginParamsNew = { ...this.state.loginParams }; let val = event.target.value; loginParamsNew[event.target.name] = val; this.setState({ loginParams: loginParamsNew }); }; login = event => { let user_id = this.state.loginParams.user_id; let user_password = this.state.loginParams.user_password; if (user_id === "admin" && user_password === "123") { localStorage.setItem("token", "T"); this.setState({ islogged: true }); } event.preventDefault(); }; render() { if (localStorage.getItem("token")) { return <Redirect to="/" />; } return ( <div className="container"> <form onSubmit={this.login} className="form-signin"> <h1 className="h3 mb-3 font-weight-normal">Please sign in</h1> <div className="row"> <div className="col"> <input type="text" name="user_id" onChange={this.handleFormChange} placeholder="Enter Username" /> <input type="password" name="user_password" onChange={this.handleFormChange} placeholder="Enter Password" /> <input type="submit" value="Login" /> </div> </div> <p>user_id === "admin" && user_password === "123"</p> </form> </div> ); } } export default Login; |
Beautify the login page with the following CSS code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | body { font-family: Arial, Helvetica, sans-serif; } * { box-sizing: border-box; } /* style the container */ .container { position: relative; border-radius: 5px; background-color: #f2f2f2; padding: 20px 0 30px 0; } /* style inputs and link buttons */ input, .btn { width: 100%; padding: 12px; border: none; border-radius: 4px; margin: 5px 0; opacity: 0.85; display: inline-block; font-size: 17px; line-height: 20px; text-decoration: none; /* remove underline from anchors */ } input:hover, .btn:hover { opacity: 1; } /* add appropriate colors to fb, twitter and google buttons */ .fb { background-color: #3b5998; color: white; } .twitter { background-color: #55acee; color: white; } .google { background-color: #dd4b39; color: white; } /* style the submit button */ input[type="submit"] { background-color: #4caf50; color: white; cursor: pointer; } input[type="submit"]:hover { background-color: #45a049; } h1 { margin-left: 30px; } p { text-align: center; } /* Two-column layout */ .col { float: left; width: 50%; margin: auto; padding: 0 50px; margin-top: 6px; } /* Clear floats after the columns */ .row:after { content: ""; display: table; clear: both; } /* vertical line */ .vl { position: absolute; left: 50%; transform: translate(-50%); border: 2px solid #ddd; height: 175px; } /* text inside the vertical line */ .vl-innertext { position: absolute; top: 50%; transform: translate(-50%, -50%); background-color: #f1f1f1; border: 1px solid #ccc; border-radius: 50%; padding: 8px 10px; } /* hide some text on medium and large screens */ .hide-md-lg { display: none; } /* bottom container */ .bottom-container { text-align: center; background-color: #666; border-radius: 0px 0px 4px 4px; } /* Responsive layout - when the screen is less than 650px wide, make the two columns stack on top of each other instead of next to each other */ @media screen and (max-width: 650px) { .col { width: 100%; margin-top: 0; } /* hide the vertical line */ .vl { display: none; } /* show the hidden text on small screens */ .hide-md-lg { display: block; text-align: center; } } |
Explanation:
- Create a state that contains “islogged” (boolean) and “loginParams“ (object) data. islogged used to notify the component if the user has successfully logged in or not. loginParams is used as an aid for charging data from elements.Why do we need islogged data? In line with the primary purpose of the React Lifecycle, the component will be re-rendered if there is a change in the state.
- Create a handleFormChange function.
handleFormChange Will receive data inputted by user, and all input will be updated into the loginParams state, which is used as a parameter for application login. - Create a login function
The login function is used to check login parameters (user and password). In this example, we use static data to check whether the user can access the dashboard page. You can change static checks by calling the rest API used to log in to this function.If the user logged, the user will get a fake-token, which is used as a key to open each protected page. - These fake tokens are stored in localstorage with the item name as “token“. Then the function will update islogged state to be “true“. On this function, react checks for changes in the state and re-renders components.
- In the rendering method, before returning the login HTML code, there is a command that check the islogged state , whether true or false; if it’s true, the system will switch to the dashboard page. In this code, we implement the Redirect component on the react-router.
- The handleFormChange function executed on the onChange event. Why the in the onChange event? The onChange event will be executed whenever there is a change in the value in the input box.
Why use localstorage to store token data ?
- Localstorage is the most straightforward built-in storage in the modern browser.
- Relatively easy.
- The majority of browsers already support them.
- Persistent. Data is still stored in the browser even though closed.
Why not use localStorage?
- Not for all use cases.
- Because of the persistence, the size of the data can increase. Need to be deleted when it reaches certain conditions.
- Store only string data.
- The old browser does not support it.
4. Create a Dashboard Page
Create a Dashboard.jsx file and copy the following code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import React, { Component } from "react"; import { Redirect, Switch, Route, Link } from "react-router-dom"; import { withRouter } from "react-router"; import "./Dashboard.css"; import Master from "./Master"; import Pos from "./Pos"; import IndexDashboard from "./IndexDashboard"; import NotFound from "./404"; class Dashboard extends Component { constructor(props) { super(props); this.state = { islogout: false }; } signOut = () => { localStorage.removeItem("token"); this.setState({ islogout: true }); }; render() { if (this.state.islogout) { return <Redirect to="/login" />; } const { match } = this.props; return ( <div> <ul> <li> <Link to={`${match.path}`}>Dashboard</Link> </li> <li> <Link to={`${match.path}/master`}>Master</Link> </li> <li> <Link to={`${match.path}/pos`}>Pos</Link> </li> <li className="push-right"> <button onClick={this.signOut} href="#"> Sign Out </button> </li> </ul> <main role="main"> <div className="main"> <Switch> <Route path={`${match.path}/master`}> <Master /> </Route> <Route path={`${match.path}/pos`}> <Pos /> </Route> <Route exact path={`${match.path}`}> <IndexDashboard /> </Route> <Route path="*"> <NotFound /> </Route> </Switch> </div> </main> </div> ); } } export default withRouter(Dashboard); |
Beautify the dashboard page with CSS code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ul { list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333; } li { float: left; } li a { display: block; color: white; text-align: center; padding: 14px 16px; text-decoration: none; } li a:hover { background-color: #00ff3b; color: white; text-decoration: none; } .push-right { float: right; line-height: 50px; } |
Explanation:
- The dashboard component has a “islogout” state. If islogout value is true, the application will redirected to the login page.
- SignOut function.
This function is used to delete fake-tokens on localstorage. Then it will update the islogout state’s value to be true. This function will be executed when the logout button is click. - In the dashboard component, we apply the HOC (High Order Component) withRouter to use the history, match, and location props.
- To get the history props on the stateful component, we must use HOC withRouter. As the stateless components, we can use react router hooks such as useHistory, useLocation, useParams and userRouteMatch.
- The match props are used to get the name of the current URL location that will be used to create a new route address. In this example, we create menus on the dashboard page, such as Master and POS.
The next step is protect your routes!
5. Creating a Private Routing Component
What is a protected route? let’s read my explanation here
Create ProtectedRoute.jsx file in /example-auth-login/src/ folder and copy the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import React from "react"; import { Route, Redirect } from "react-router-dom"; const ProtectedRoute = ({ children, ...rest }) => { return ( <Route {...rest} render={({ location }) => localStorage.getItem("token") ? ( children ) : ( <Redirect to={{ pathname: "/login", state: { from: location } }} /> ) } /> ); }; export default ProtectedRoute; |
Explanation:
The protected route is the main component in creating protection for a page. The rendering component will check whether the localstorage item already has the token item? If yes, the route will continue on the destination page. Otherwise, react will call the Redirect component to redirect to the login page.
6. Edit app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import React from "react"; import "./styles.css"; import Login from "./Login"; import Dashboard from "./Dashboard"; import { BrowserRouter as Router, Switch, Route, Redirect } from "react-router-dom"; import ProtectedRoute from "./ProtectedRoute"; export default function App() { return ( <Router> <Switch> <Route path="/login"> <Login /> </Route> <ProtectedRoute path="/dashboard"> <Dashboard /> </ProtectedRoute> <Route exact path="/"> <Redirect exact from="/" to="dashboard" /> </Route> <Route path="*"> <Redirect from="/" to="dashboard" /> </Route> </Switch> </Router> ); } |
Explanation:
In this component, the browserRouter applied. browserRouter must be applied to the root’s component.
How do you implement a protected route component in react?
ProtectedRoute is applied to the page that will be given access rights. In this example, the dashboard component.
7. Run the Application
Application has been completed, we can try it by running the command
1 | npm start |
For demo login → user : admin and password : 123
To try it online, you can use the following codesandbox.
Conclusion
To create a private routing, you need to create a ProtectedRoute helper component. The above application is a basic method of creating a user authenticates page. For broader application projects, you need both context and redux for better code writing.
Hopefully, this basic tutorial makes the login page and protected routes in react. Read another ReactJS tutorial for beginners here
Nicely explained. Is there any Github code link or how can i get complete code for this.
Open my sandbox code
https://seegatesite.com/implement-login-page-and-protected-route-reactjs/#7_Run_the_Application
LocalStorage on the browser is WIDELY KNOWN to be extremely vulnerable to XSS and hence leaving you completely vulnerable. At the outset basically therefore there is NO security whatsoever for your system. You should do your research before writing an article that will send people down the wrong path when implementing ESSENTIAL & SENSITIVE areas of a system.