Django Rest Framework with ReactJS and Swagger UI implementation: Part2

shorya sharma
17 min readMar 14, 2021

--

This is the 2nd part of the two-part series tutorial, in this, we will create a simple ReactJS web app to consume our DRF APIs from the last part.

https://sharmashorya1996.medium.com/django-rest-framework-with-reactjs-and-swagger-ui-implementation-part1-9e02c8fb4b45

I won’t be going in-depth about ReactJS and how to make a web app using it. So if you are new to React or you are not familiar with the terminologies in react you can skip this post.

This is a code-along tutorial, we will use TypeScript as our coding language and will be using react-redux.

PREREQUISITES

  1. ReactJS
  2. TypeScript/JavaScript
  3. Redux
  4. Axios

Let’s get started with part 2

STEPS

  1. In any folder of your choice run the following command in the terminal.
npx create-react-app drfreactapp

where “drfreactapp” is the name of the project, you can keep any name for the same.

2. Now let's install the packages that will be useful in our app

npm install 'react-router-dom'
npm install redux
npm install redux-promise
npm install redux-thunk
npm install react-redux
npm install encrypt-password
npm install axios

3. Now in the editor of your choice, I would suggest VScode, inside the src folder make the following folders:

Remember, This is my way of structuring the code, You are free to use any structuring you want to or not use any structuring at all.

a) components: In this, we will make our components like login, signup, and landingPage.

b) actions: This will contain the actions part of the react-redux

c) reducers: This will contain all the necessary reducers of our app

d) routes: This will contain the routes to our pages along with the private route.

e) css: This will contain the CSS part of our web App.

f) common: This will contain the common function like password encryption and validations for our web app.

g) store: This will be used to configure the store for our redux-store in our webapp.

h) utils: This will contain the various utility components that will be used all across the web app like request endpoint URL, App messages, app errors, and Axios API requests by various functions.

4. Now inside the common folder make two files with the name requireAuth.tsx and util.tsx.

Inside the requireAuth.tsx file, write the following code

export const logout = () => {
window.location.assign("/logout");
}

Inside the util.tsx file, write the following code.

import EncryptPassword from '../../node_modules/encrypt-password/index'export const navigate = (pathname, props) => {
return props.history.push(pathname);
}
/*** Validate password - min charac 8 number numeric with special key and number* @param password - Password as string*/export const validatePassword = (password) => {let validatePassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;return validatePassword.test(password);}export const userName = (name) => {let userName = /\S/;let userName1 = /^[a-zA-Z].*[\s\.]*$/;if(userName.test(name) === true && userName1.test(name) === true){return true
}
else{
return false}}
/**** @param {email} email*/export const emailValidation = (email) => {let emailRegex = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/return emailRegex.test(email);}export const getCypherString = (plainText) => {const encryptedPassword = EncryptPassword(plainText, 'Compunnel');return encryptedPassword;}export const getCookie = (name) => {let cookieArr = document.cookie.split(";");for(let i = 0; i < cookieArr.length; i++) { let cookiePair = cookieArr[i].split("="); if(name == cookiePair[0].trim()) { return (cookiePair[1]);
}}
return "null";
}

5. Inside the store folder create a file with the name configureStore.tsx and write the following code.

import { createStore, applyMiddleware, compose } from 'redux';import promise from 'redux-promise';import thunk from 'redux-thunk';// import logger from 'redux-logger';import rootReducer from '../reducers';declare const window: any;export default function configureStore(initialState) {const finalCreateStore = compose<any>(applyMiddleware(promise, thunk),window.devToolsExtension ? window.devToolsExtension() : f => f)(createStore);const store = finalCreateStore(rootReducer, initialState);return store;}

6. Now convert your App.jsx file to App.tsx ( also convert all the other .jsx/.js files to .tsx)

you can use this command to convert the file from .jsx to .tsx

npx react-js-to-ts "filename without quotes".js

Write the following code inside App.tsx.

import React, { Fragment } from 'react';import './App.css';import './css/App.css';import AppRoute from './routes';function App() {return (<div><AppRoute /></div>);}export default App;

7. Now inside your css folder create a file App.css and write the following code inside it.

body {font-family: Arial, Helvetica, sans-serif;background-color: black;}* {box-sizing: border-box;}/* Add padding to containers */.container {padding: 16px;background-color: white;}/* Full-width input fields */input[type=text], input[type=password] {width: 100%;padding: 15px;margin: 5px 0 22px 0;display: inline-block;border: none;background: #f1f1f1;}input[type=text]:focus, input[type=password]:focus {background-color: #ddd;outline: none;}/* Overwrite default styles of hr */hr {border: 1px solid #f1f1f1;margin-bottom: 25px;}/* Set a style for the submit button */.registerbtn {background-color: #4CAF50;color: white;padding: 16px 20px;margin: 8px 0;border: none;cursor: pointer;width: 100%;opacity: 0.9;}.registerbtn:hover {opacity: 1;}/* Add a blue text color to links */a {color: dodgerblue;}/* Set a grey background color and center the text of the "sign in" section */.signin {background-color: #f1f1f1;text-align: center;}.styled-table {border-collapse: collapse;margin: 25px 0;font-size: 0.9em;font-family: sans-serif;min-width: 400px;box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);}.styled-table thead tr {background-color: #009879;color: #ffffff;text-align: left;}.styled-table th,.styled-table td {padding: 12px 15px;}.styled-table tbody tr {border-bottom: 1px solid #dddddd;}.styled-table tbody tr:nth-of-type(even) {background-color: #f3f3f3;}.styled-table tbody tr:last-of-type {border-bottom: 2px solid #009879;}.styled-table tbody tr.active-row {font-weight: bold;color: #009879;}

8. Now inside the index.tsx file, write the following code:

import React from 'react';import ReactDOM from 'react-dom';import './css/App.css';import App from './App';import * as serviceWorker from './serviceWorker';import { Provider } from "react-redux";import initializeStore from "./store/configureStore";import { Router } from "react-router-dom";import createBrowserHistory from "history/createBrowserHistory";import "react-app-polyfill/ie11";import "react-app-polyfill/stable";var initialState:any;const store = initializeStore(initialState);const history = createBrowserHistory();ReactDOM.render(<React.StrictMode><Provider store={store}><Router history={history}><App /></Router></Provider></React.StrictMode>,document.getElementById('root')
);
serviceWorker.unregister();

9. Now inside the utils folder create files with the name appConfig.tsx, appEndpoints.tsx, appErrors.tsx, appmessages.tsx and httpRequest.tsx.

Inside the appConfig.tsx we provide URL where our Django APIs are running. So write the following single line of code inside it.

export const ROOT_VERSION_DEVCORE = "http://127.0.0.1:8000";

we are running our APIs on localhost port 8000

Inside appEndpoints.tsx we will provide the endpoints of our DRF APIs, which we will use to communicate with our backend. write the following lines in the file

export const ENDPOINTS = {
LOGINUSER1: "loginapi/",
SIGNUP: "signupapi/",
GETUSERDETAILS:"userdetails/"
}

Inside the appErrors.tsx we list some errors raised by the webApp , write the following lines of code inside it.

import { ENDPOINTS } from '../utils/appEndpoints'export const AppErrorMessages = {
[ENDPOINTS.SIGNUP]:'Something went wrong. Please try signing up again.',
[ ENDPOINTS.LOGINUSER1]:'Incorrect credentials. Please enter valid credentials and try again',
}

Inside appmessages.tsx we write the message responses we show in the UI.

export const AppMessages = {
U0001: 'Username cannot be blank',
U0002: 'Password cannot be blank',
U0003: 'NewPaswword can not be blank',
U0004: 'ConfirmPaswword can not be blank',
U0005: 'Password must contain 8 characters including an uppercase alphabet, lowercase alphabet, one special character and a numeric digit ',
U0006: 'New Password and Confirm Password does not match',
U0007: 'Password fields cannot be blank',
U0008: 'Password changed . Enjoy!',
U0009: 'Email cannot be left blank.',
U0010: ' is not a valid email address. Please enter the correct email id.',
U0012: 'Something went wrong!',
U0013: 'is not a valid email address. Please enter the correct email id.',
U0024: 'No Record Found',
U0032: 'This field is required.',
U0033: 'Looks like no users has been created yet!',
U0034: 'Enter valid email address',
U0039: 'Password and Confirm Password does not match',
U0040: 'Signup completed and an email has been sent to you. Enjoy!',
U0087: 'Incorrect credentials. Please enter valid credentials and try again',
}

Now comes an important file httpRequest.tsx, this file will be used my majorly all the components as it contains the code to communicate with APIs using the Axios library. This is a good practice to create a single point repository for all methods (GET, POST, PUT, DELETE) which can be used by all the components instead of writing Axios functions at every component.

Write the following lines of code inside the httpRequest.tsx file

import axios from "axios";import { ROOT_VERSION_DEVCORE } from "../utils/appConfig";import { logout } from "../common/requireAuth";axios.interceptors.response.use(async function(response) {return response;},function(error) {if (error && error.response) {if (error.response.status === 401) {logout();}}return Promise.reject(error);});export function doLoginPost(config) {if (config) {return axios({url: `${ROOT_VERSION_DEVCORE}/` + config.url,method: "POST",data: config.data,headers: {"Content-Type": 'application/json' , Accept:'application/json'}});}}export function doActionGetDevCore(config) {const getToken = JSON.parse(localStorage.getItem("user") as any);if (config) {return axios({url: `${ROOT_VERSION_DEVCORE}/` + config.url,method: "GET",headers: {Authorization: "Bearer " + (getToken ? getToken.data.token : "")}});}}export function doActionDevCore(config, method) {const getToken = JSON.parse(localStorage.getItem("user") as any);if (config) {return axios({url: `${ROOT_VERSION_DEVCORE}/` + config.url,method: method,data: config.data,headers: {//'Content-type': 'application/json',Authorization: "Bearer " + (getToken ? getToken.data.token : "")}
});
}}export function doActionPostDevCore(config) {return doActionDevCore(config, "POST");}export function doActionPatchDevCore(config) {return doActionDevCore(config, "PATCH");}export function doActionPutDevCore(config) {return doActionDevCore(config, "PUT");}export function doActionDelDevCore(config){return doActionDevCore(config, "DELETE");}export function doLoginGet(config) {if (config) {return axios({url: `${ROOT_VERSION_DEVCORE}/` + config.url,method: "GET",data: config.data,headers: {}});
}}

10. Now let's make our components, inside the components folder create the files with the name Landing.tsx, landingPage.tsx, loginForm.tsx, loginPageComponent.tsx, signupContainer.tsx, and signupPage.tsx.

Inside the signupPage.tsx write the following lines of code:

import React, { Fragment } from 'react';const SignupPage = (props: any) => {return (<div>{props.isSignup ?<Fragment><form><div className="container"><h1>Register</h1><p>Please fill in this form to create an account.</p><hr /><label><b>Email</b></label><input type="text" placeholder="Enter Email" name="Email" id="email" required  onChange={(e) => props.handleChange("EmailAddress", e)} value={props.fields["EmailAddress"]}/><label >{props.errors["EmailAddress"]}</label><br></br><label><b>First Name</b></label><input type="text" placeholder="First Name" name="FirstName" id="FirstName" required onChange={(e) => props.handleChange("FirstName", e)} value={props.fields["FirstName"]}/><label >{props.errors["FirstName"]}</label><br></br><label><b>User Name</b></label><input type="text" placeholder="Enter username" name="LastName" id="UserName" required onChange={(e) => props.handleChange("LastName", e)} value={props.fields["LastName"]}/><label>{props.errors["LastName"]}</label><br></br><label><b>Password</b></label><input name="password" type="password" onChange={(e) => props.handleChange("Password", e)} value={props.fields["Password"]} placeholder="Enter password" /><label >{props.errors["password"]}</label><br></br><label><b>Repeat Password</b></label><input name="ConfirmPassword" type="password" onChange={(e) => props.handleChange("ConfirmPassword", e)} value={props.fields["ConfirmPassword"]} placeholder="Re-enter password" required /><label>{props.errors["ConfirmPassword"]}</label><br></br><hr /><p>By creating an account you agree to our <a href="#">Terms & Privacy</a>.</p><button type="submit" className="registerbtn" onClick={(e) =>props.signupSumit(e)}>Register</button></div><div className="container signin"><p>Already have an account?<button type="submit" className="registerbtn" onClick = {props.handleExpired}>Sign In</button></p></div></form></Fragment>:<Fragment><form><div className="container signin"><p>Already have an account?<button type="submit" className="registerbtn" onClick = {props.handleLogin}>Sign In</button></p></div></form></Fragment>}</div>);}export default SignupPage;

Inside the signupContainer.tsx write the following lines of code:

import React, { Component, Fragment } from 'react';import SignupPage from './signupPage';import { validatePassword,getCypherString, emailValidation } from '../common/util'import { connect } from 'react-redux';import { withRouter } from 'react-router-dom';import { signUpUser, signUpUserSuccess, signUpUserFailure } from '../actions/loginAction';import { AppMessages } from '../utils/appmessages';export class SignupContainer extends Component<any,any> {constructor(props:any) {super(props);this.state = {fields: {},errors: {},profileImg: "",ProfilePicture: null,signUpCompleted: false,imageBase64String: "",brandColor: '',brandLogo: '',brandLogoName: '',notification: false,isSignup:true}}handleValidation = (arr1:any) => {let fields = this.state.fields;let errors :any = {};let formIsValid = true;for(let i=0;i<arr1.length;i++){if(!fields[arr1[i]]){formIsValid = false;errors[arr1[i]] = AppMessages.U0032;}}if (typeof fields["EmailAddress"] !== "undefined") {if (!emailValidation(fields["EmailAddress"])) {formIsValid = false;errors["EmailAddress"] = AppMessages.U0034;}}if (typeof fields["Password"] !== "undefined") {if (!validatePassword(fields["Password"])) {formIsValid = false;errors["Password"] = AppMessages.U0005;}}if (typeof fields["ConfirmPassword"] !== "undefined") {if (fields["Password"] !== fields["ConfirmPassword"]) {formIsValid = false;errors["ConfirmPassword"] = AppMessages.U0039;}}this.setState({ errors: errors });return formIsValid;}handleChange = (field:any, event:any) => {let fields = this.state.fields;let errors = this.state.errors;fields[field] = event.target.value;errors[field] = "";this.setState({ fields, errors });}signupSumit = (e:any) => {e.preventDefault();let arr1 = ["EmailAddress","FirstName","LastName","Password","ConfirmPassword"]if (this.handleValidation(arr1)) {let formData = {email:this.state.fields.EmailAddress,fname: this.state.fields.FirstName,username:this.state.fields.LastName,password: getCypherString(this.state.fields.Password),}this.props.signupUser(formData).then((res:any) => {if (res) {this.setState({ signUpCompleted: true, isSignup: false, res: {} })}else {this.setState({ signUpErr: true})}}).catch((err:any) => {this.setState({ signUpErr: true})});} else {return false;}}closeMessageBar = () => {this.setState({ signUpErr: false,notification: false, fields : {EmailAddress : "",FirstName:"",LastName:"",Password:"",ConfirmPassword:""} })}handleLogin = () => {this.props.history.push("/login");}render() {return (<Fragment><SignupPage{...this.state}signupSumit={this.signupSumit}handleChange={this.handleChange}closeMessageBar={this.closeMessageBar}handleLogin = {this.handleLogin}/></Fragment>);}}export const mapDispatchToProps = (dispatch:any) => {return {signupUser: (data:any) => {return new Promise((resolve, reject) => {dispatch(signUpUser(data)).then((res:any) => {dispatch(signUpUserSuccess(res.payload.data));resolve(res.payload.data)}).catch((err:any) => {dispatch(signUpUserFailure(err))reject(err);})
});
}}}
export default connect(null, mapDispatchToProps)(withRouter(SignupContainer));

Inside the loginForm.tsx write the following lines of code:

import React, { Fragment } from 'react';const LoginForm = (props: any) => {return (<form onSubmit={props.loginUser}><div className="container"><h1>Login</h1><hr /><label><b>Username</b></label><input type="text" value={props.username} placeholder="Enter username" name="username" onChange={props.handleChange}/><br></br><label><b>Password</b></label><input type="password" value={props.password} placeholder="Enter password" name="password" onChange={props.handleChange} /><button type="submit" className="registerbtn">Login</button></div><div className="container"><button type="submit" className="registerbtn" onClick = {props.handleSignup}>Sign Up</button></div></form>);}export default LoginForm;

Inside the loginPageComponent.tsx write the following lines of code:

import React from 'react';
import LoginForm from './loginForm';
import { connect } from 'react-redux';
import { loginUser, loginUserSuccess, loginUserFailure } from '../actions/loginAction';
import { AppMessages } from '../utils/appmessages';
import { withRouter } from 'react-router-dom';
import {validatePassword, getCypherString } from '../common/util';
import jwt_decode from 'jwt-decode';
export class LoginComponent extends React.Component<any, any> {constructor(props: any) {super(props);this.state = {username: '',password: '',error: null,errorMsg: '',loginProgress: false,brandColor: '',brandLogo: '',brandLogoName: '',brandType:''}}componentDidMount() {}loginUser = (e) => {e.preventDefault();if (!this.state.username) {this.setState({ error: true, errorMsg: AppMessages.U0001 })return false;} else if (!this.state.password) {this.setState({ error: true, errorMsg: AppMessages.U0002 })return false;}else if (!validatePassword(this.state.password)){this.setState({ error: true, errorMsg: AppMessages.U0087 })return false;}else {this.setState({ loginProgress: true })}this.props.loginUser({ email: this.state.username, password: getCypherString(this.state.password)}).then(res => {if (res.data.token) {localStorage.setItem('user', JSON.stringify({data:res.data}));this.props.history.push("/landingpage");}else {this.setState({ error: true});return false;}}).catch(err => {this.setState({ error: true});});}handleChange = (event) => {const { name, value } = event.target;let trimmedValue = value.trim();this.setState({ [name]: trimmedValue });if (this.state.error) {this.setState({ error: false });}}handleSignup = () => {this.props.history.push("/signup");}render() {return (<LoginForm{...this.state}handleChange={this.handleChange}loginUser={this.loginUser}handleSignup={this.handleSignup}/>);}}export const mapDispatchToProps = (dispatch) => {return {loginUser: (userInfo) => {return new Promise((resolve, rej) => {dispatch(loginUser(userInfo)).then(res => {dispatch(loginUserSuccess(res.payload.data))resolve(res.payload.data)}).catch(err => {dispatch(loginUserFailure(err))rej(err)})
})
}}}
export default connect(null, mapDispatchToProps)(withRouter(LoginComponent));

Inside the Landing.tsx component write the following lines of code:

import React, { Fragment } from 'react'const Landing = (props:any) =>{let jj=0;let userDetails: any = []for(jj=0;jj<props.students.length;jj++){userDetails.push(<tr><td><p>{props.students[jj].userid}</p></td><td><p>{props.students[jj].email}</p></td><td><p> {props.students[jj].fname}</p></td><td><p> {props.students[jj].username}</p></td></tr>)}return (<div className="container styled-table"><table><thead><tr><th>userid</th><th>email</th><th>fname</th><th>username</th></tr></thead></table><div ><table>{userDetails}</table></div></div>)}
export default Landing;

Inside the LandingPage.tsx write the following lines of code:

import React, { Fragment } from 'react';import { connect } from 'react-redux';import { withRouter } from 'react-router-dom';import { FetchUserDetails, FetchUserDetailsFailure, FetchUserDetailsSuccess } from '../actions/usersAction';import Landing from './Landing';export class LandingPageComponent extends React.Component<any, any>{constructor(props) {super(props)this.state = {students: [],loading:true}}componentDidMount(){this.getDetails()}getDetails = () =>{this.props.FetchUserDetails().then(response => {if(response.isSuccess){this.setState({students:response.data, loading:false})}else{this.setState({students:[]})}}).catch(() => {this.setState({students:[]})});return false;}render() { //Whenever our class runs, render method will be called automatically, it may have already defined in the constructor behind the scene.return (<Fragment><Landing {...this.state}/></Fragment>)}}export const mapDispatchToProps = (dispatch) => {return {FetchUserDetails: () => {return new Promise((resolve, rej) => {dispatch(FetchUserDetails()).then(res => {dispatch(FetchUserDetailsSuccess(res.payload.data))resolve(res.payload.data)}).catch(err => {dispatch(FetchUserDetailsFailure(err))rej(err)})
})}}}
export default withRouter(connect(null, mapDispatchToProps)(withRouter(LandingPageComponent)));

11. Now inside the actions folder create files with name loginAction.tsx and usersAction.tsx

Inside loginAction.tsx file write the following lines of code:

import { doLoginPost} from '../utils/httpRequest';import { ENDPOINTS } from '../utils/appEndpoints'export const USER_DETAILS = "USER_DETAILS";export const USER_DETAILS_SUCCESS = "USER_DETAILS_SUCCESS";export const USER_DETAILS_FAILURE = "USER_DETAILS_FAILURE";export const INVITELINK = "INVITELINK";export const INVITELINK_SUCCESS = "INVITELINK_SUCCESS";export const INVITELINK_FAILURE = "INVITELINK_FAILURE";export const SIGNUPUSER = "SIGNUPUSER";export const SIGNUPUSER_SUCCESS = "SIGNUPUSER_SUCCESS";export const SIGNUPUSER_FAILURE = "SIGNUPUSER_FAILURE";export const LOGOUTUSER = "LOGOUTUSER";export const LOGOUTUSER_SUCCESS = "LOGOUTUSER_SUCCESS";export const LOGOUTUSER_FAILURE = "LOGOUTUSER_FAILURE";export const GETUSERDATABYGUID = "GETUSERDATABYGUID";export const GETUSERDATABYGUID_SUCCESS = "GETUSERDATABYGUID_SUCCESS";export const GETUSERDATABYGUID_FAILURE = "GETUSERDATABYGUID_FAILURE";/*** action to post user credentials* @param(@userDetails)*/export const loginUser = (userDetails) => {const request = doLoginPost({ url: ENDPOINTS.LOGINUSER1, data: userDetails });return {type: USER_DETAILS,payload: request};}/*** action to save user credentials*/export const loginUserSuccess = (userDetails) => {return {type: USER_DETAILS_SUCCESS,payload: userDetails};}export const loginUserFailure = (err) => {return {type: USER_DETAILS_FAILURE,payload: err};}export const postInviteLink = (data) => {const request = doLoginPost({ url: `users/isinvitelinkValid/${data}` });return {type: INVITELINK,payload: request,};}export const postInviteLinkSuccess = (loanObj) => {return {type: INVITELINK_SUCCESS,payload: loanObj};}export const postInviteLinkFailure = (error) => {return {type: INVITELINK_FAILURE,payload: error};}export const signUpUser = (signUpDetails) => {const request = doLoginPost({ url: ENDPOINTS.SIGNUP, data: signUpDetails });return {type: SIGNUPUSER,payload: request,};}export const signUpUserSuccess = (signUpDetails) => {return {type: SIGNUPUSER_SUCCESS,payload: signUpDetails};}export const signUpUserFailure = (error) => {return {type: SIGNUPUSER_FAILURE,payload: error};}

Inside usersAction.tsx file write the following lines of code:

import { doActionGetDevCore} from '../utils/httpRequest';import { ENDPOINTS } from '../utils/appEndpoints'export const USER_DETAILS = "USER_DETAILS";export const USER_DETAILS_SUCCESS = "USER_DETAILS_SUCCESS";export const USER_DETAILS_FAILURE = "USER_DETAILS_FAILURE";export const FetchUserDetails = () => {const request = doActionGetDevCore({ url:ENDPOINTS.GETUSERDETAILS});return {type: USER_DETAILS,payload: request};}export const FetchUserDetailsSuccess = (setting) => {return {type: USER_DETAILS_SUCCESS,payload: setting};}export const FetchUserDetailsFailure = (err) => {return {type: USER_DETAILS_FAILURE,payload: err};}

12. Now inside the reducers folder create files with the name index.tsx and LoginReducer.tsx

Inside the index.tsx folder, write the following lines of code:

import { combineReducers } from 'redux';// import { routerReducer } from 'react-router-redux';import loginReducer from './LoginReducer';const rootReducer = combineReducers({loginReducer: loginReducer,});export default rootReducer;

Inside the LoginReducer.tsx write the following lines of code:

import {USER_DETAILS, USER_DETAILS_SUCCESS, USER_DETAILS_FAILURE, LOGOUTUSER} from "../actions/loginAction";const INITIAL_STATE = {userDetails: {}}const dict = {[USER_DETAILS]: (state = INITIAL_STATE) => ({...state,userDetails: {userDetails: undefined,error: null,loading: true}}),[USER_DETAILS_SUCCESS]: (state = INITIAL_STATE, action:any = {}) => ({...state,userDetails: {userDetails: action.payload,error: null,loading: false}}),[USER_DETAILS_FAILURE]: (state = INITIAL_STATE, action:any = {}) => ({...state,userDetails: {userDetails: undefined,error: action.payload.message,loading: false}}),[LOGOUTUSER]: (state = INITIAL_STATE) => ({...state,userDetails: {userDetails: undefined,loading: false}})};export default function (state = INITIAL_STATE, action:any = {}) {const delegate = dict[action.type];if (!!delegate) {return delegate(state, action);}return state;}

13. Now we move to the final steps of providing the routes to the component, for that, move inside the routes folder and create files with the name index.tsx and privateRoute.tsx.

Inside the index.tsx file, write the following lines of code:

import React, { Component } from "react";import { Route, Switch, Redirect, BrowserRouter } from 'react-router-dom';import App from '../App'import LandingPageComponent from "../components/landingPage";import LoginComponent from "../components/loginPageComponent";import SignupContainer from "../components/signupContainer";import PrivateRoute from "./privateRoute";export default class AppRoute extends Component {render() {return (<BrowserRouter><Switch><Route path={"/"} exact component={App} ><Redirect from='/' to='/login' /></Route><Route exact path='/login' component={LoginComponent} /><Route path='/signup' exact component={SignupContainer} /><PrivateRoute path='/landingpage' exact component={LandingPageComponent} /></Switch></BrowserRouter>)}}

Inside the privateRoute.tsx file, write the following lines of code:

import React from 'react'import { Redirect, Route } from 'react-router-dom'const PrivateRoute = ({ component: Component, ...rest }) => {const isLoggedIn = localStorage.getItem("user") != null ? true : false;return (<Route{...rest}render={props =>isLoggedIn ? (<Component {...props} />) : (<Redirect to={{ pathname: '/login', state: { from: props.location } }} />)}/>)}export default PrivateRoute

14. Now, before running our webApp make sure you have a file name tsconfig.json in your application outside the src folder, If not then create one and write the following lines of code inside it.

{"compilerOptions": {"target": "es5","lib": ["dom","dom.iterable","esnext"],"noImplicitAny": false,"allowJs": true,"skipLibCheck": true,"esModuleInterop": true,"allowSyntheticDefaultImports": true,"strict": true,"forceConsistentCasingInFileNames": true,"module": "esnext","moduleResolution": "node","resolveJsonModule": true,"isolatedModules": true,"noEmit": true,"jsx": "react","noFallthroughCasesInSwitch": true},"include": ["src"]}

Also, if you think your tsconfig.json is different than mine, feel free to replace your code with the above one.

15. Now we are all set to run our webApp

npm start

In case you are facing some issues while running make sure all the dependencies are correctly installed, I am sharing my package.json, you can simply replace it with mine and run the command

npm install
npm start

here is my package.json

{"name": "landingpage","version": "0.1.0","private": true,"dependencies": {"@testing-library/jest-dom": "^4.2.4","@testing-library/react": "^9.5.0","@testing-library/user-event": "^7.2.1","@types/d3": "^6.0.0","@types/jest": "^26.0.13","@types/jwt-decode": "^2.2.1","@types/react-dom": "^16.9.9","@types/react-router-dom": "^5.1.5","@uifabric/react-cards": "^0.110.22","axios": "^0.20.0","encrypt-password": "^1.0.1","enzyme": "^3.11.0","enzyme-adapter-react-16": "^1.15.2","enzyme-to-json": "^3.4.4","fetch-mock": "^7.3.9","jwt-decode": "^2.2.0","moment": "^2.27.0","node-fetch": "^2.6.0","node-sass": "^4.14.1","react": "^16.13.1","react-avatar-uploader": "^1.0.3","react-dom": "^16.13.1","react-redux": "^7.2.1","react-router-dom": "^5.2.0","react-scripts": "3.4.1","redux": "^4.0.4","redux-api-middleware": "^3.2.1","redux-logger": "^3.0.6","redux-mock-store": "^1.5.3","redux-promise": "^0.6.0","redux-thunk": "^2.3.0","sinon": "^9.0.2","styled-components": "^5.1.1","typescript": "^3.9.5"},"devDependencies": {"@types/react-redux": "^7.1.9"},"scripts": {"start": "react-scripts start","build": "react-scripts build","test": "react-scripts test --coverage","test:watch": "jest --watch","eject": "react-scripts eject","test:tsc": "tsc -p tsconfig.test.json -w","coverage": "react-scripts test --coverage","test:debug": "react-scripts --inspect-brk test --runInBand --no-cache"},"eslintConfig": {"extends": "react-app"},"browserslist": {"production": [">0.2%","not dead","not op_mini all"],"development": ["last 1 chrome version","last 1 firefox version","last 1 safari version"]}}

Let's Test our web App,

  1. Signup in our application by filling in the details and clicking on Register

2. Click on Sign In and you will be routed to the login page

3. Fill in the details and click on Login, you will be redirected to LandingPage where your details will be listed.

Note: We have only used two API methods in this web App , which are POST and GET, you can use PUT and DELETE for updating passwords and removing userDetails, etc.

Also If you interested In building APIs using flask restplus, check out this tutorial of mine:
https://sharmashorya1996.medium.com/flask-restplus-swagger-token-authentication-blueprints-84272b6bcef1

--

--

shorya sharma
shorya sharma

Written by shorya sharma

Assistant Manager at Bank Of America | Ex-Data Engineer at IBM | Ex - Software Engineer at Compunnel inc. | Python | Data Science

No responses yet