DEV Community

Cover image for Basic Routing & Auth in React
Brama Udi
Brama Udi

Posted on

Basic Routing & Auth in React

In this articles i'm using CRA (create-react-app) to create React project.

npm init react-app ProjectName # or npx create-react-app ProjectName # if using yarn yarn create react-app ProjectName 
Enter fullscreen mode Exit fullscreen mode

Install Dependency

The project created by CRA is just only contains React core so if we want to do routing we'll need to install a package called react-router-dom.

npm i react-router-dom # or yarn add react-router-dom 
Enter fullscreen mode Exit fullscreen mode

Router Configuration

I'm usually make a seperate folder called router to handle all my routing files, so let create this inside our src dir.

After that we will create an index.js file inside router folder then paste this;

// src/router/index.js import React, { Suspense, lazy } from 'react' import { Switch, Route } from 'react-router-dom' // navigation guard / middleware import { PrivateRoute } from './_private' import { GuestRoute } from './_guest' // Preloading component const Loading = () => <h1>Loading ...</h1> // Import helper func const views = (path) => { return lazy(() => import(`../views/${path}`)) } // route list const routes = [ { path: '/', component: 'index' }, { path: '/login', component: 'login', guest: true }, { path: '/secret', component: 'secret', private: true }, { path: '404', // 404 fallback noExact: true, // all route "exact" by default component: '404' } ] const router = () => ( <Suspense fallback={<Loading />}> <Switch> {routes.map((route, index) => { if (route.path !== '404') { if (route.private) { return route.noExact ? <PrivateRoute key={index} path={route.path} component={views(route.component)} /> : <PrivateRoute key={index} exact path={route.path} component={views(route.component)} /> } else if (route.guest) { return route.noExact ? <GuestRoute key={index} path={route.path} component={views(route.component)} /> : <GuestRoute key={index} exact path={route.path} component={views(route.component)} /> } else { return route.noExact ? <Route key={index} path={route.path} component={views(route.component)} /> : <Route key={index} exact path={route.path} component={views(route.component)} /> } } else { return <Route key={index} component={views(route.component)} /> } })} </Switch> </Suspense> ) export default router 
Enter fullscreen mode Exit fullscreen mode

In the code above we importing Suspense and lazy module, this is useful for lazy loading / code-splitting our React component for performant purpose and it also will displaying a loading placeholder component at loading time.

With the configuration like that we will have something like route rules like this;

Prop. Name Required Value Info
path String Router URL
component String Views component
private × Boolean Only logged user can access
guest × Boolean Only guest can access
noExact × Boolean All routes all exact by default, so it can be optional for specified use

To make the navigation guard work as expected we have to make a middleware for the private route and guest-only route.

So create a file named _private.js;

// src/router/_private.js import React from 'react' import { Route, Redirect } from 'react-router-dom' import Auth from '../store/auth' const FALLBACK = '/login' export const PrivateRoute = ({ component: Component, ...rest}) => { return ( <Route {...rest} render={props => { if (Auth.state.logged) { return <Component {...props} /> } else { return ( <Redirect to={{ pathname: FALLBACK, state: { from: props.location } }} /> ) } }} /> ) } 
Enter fullscreen mode Exit fullscreen mode

and create file named _guest.js too then paste the code above but with these following changes;

- if (Auth.state.logged) { + if (!Auth.state.logged) { 
Enter fullscreen mode Exit fullscreen mode
- const FALLBACK = '/login' + const FALLBACK = '/secret' 
Enter fullscreen mode Exit fullscreen mode
- export const PrivateRoute = ({ component: Component, ...rest}) => { + export const GuestRoute = ({ component: Component, ...rest}) => { 
Enter fullscreen mode Exit fullscreen mode

Store Configuration

Here the store are plain javascript where used to handle auth state variable that will be global state.

I'm personally use folder named store to store all my "state-handle file", do the same thing with auth.js;

// src/store/auth.js const state = { logged: !!localStorage.getItem('token') } const actions = { login: () => { return new Promise((resolve, reject) => { localStorage.setItem('token', JSON.stringify({ // In real world token is obtained from api request token: 'abcdef321654' })) resolve() }) }, logout: () => { return new Promise((resolve, reject) => { localStorage.removeItem('token') resolve() }) } } export default { state, actions } 
Enter fullscreen mode Exit fullscreen mode

Views Component

Now we should write the views components where these will be a our pages display, all these files are located in views dir;

index.js

import React from 'react' const Index = () => <h1>Index Page</h1>  export default Index 
Enter fullscreen mode Exit fullscreen mode

login.js

import React from 'react' import Auth from '../store/auth' const handleLogin = (props) => { Auth.actions.login().then( () => { props.history.push('/secret') } ) .catch( () => alert('Login failed') ) } const Login = (props) => ( <div> <h1>Login Page</h1>  <button onClick={() => handleLogin(props)}>Login</button>  </div> ) export default Login 
Enter fullscreen mode Exit fullscreen mode

secret.js

import React from 'react' import Auth from '../store/auth' const handleLogout = (props) => { Auth.actions.logout().then( () => { props.history.push('/login') } ) } const Secret = (props) => ( <div> <h1>Secret Page</h1>  <button onClick={() => handleLogout(props)}>Logout</button>  </div> ) export default Secret 
Enter fullscreen mode Exit fullscreen mode

Ok, save all and run the project.

npm run start # or "yarn start" 
Enter fullscreen mode Exit fullscreen mode

Because we didn't make a navigation links, so we've to manually navigate between the pages by editing the url in address bar to test the route XD

Our project structure should look like this;

├── package.json ├── public # your static content │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── README.md └── src # our working directory ├── App.css ├── App.js ├── index.js ├── router # routing files │ ├── _guest.js │ ├── index.js │ └── _private.js ├── serviceWorker.js ├── setupTests.js ├── store # state │ └── auth.js └── views # views component ├── index.js ├── login.js └── secret.js 
Enter fullscreen mode Exit fullscreen mode

Some of React testing files like App.test.js, App.css, are not included.

I also have created the repo for this, you can visit the link in down below for more practice:

https://github.com/bramaudi/react-boilerplate

Top comments (1)

Collapse
 
bramaudi profile image
Brama Udi

I hope it does not steal someone idea.