So, in the previous blog, we successfully made API using django, where users are able to register and login by generating a token. Now, we need to implement this in our frontend to be used in the browser.
Create a frontend
folder and cd
into it
Install react in a new terminal: npx create-react-app my-app
This creates another folder called my-app
and dependencies are installed. This may take a while!
Once it's complete, cd
into my-app
and see if it works:npm start
Now, make a Reducer
folder containing an auth.js
file and index.js
. You also need an Actions
folder containing types.js
and another auth.js
file. Create a store.js
file too.
In Actions/types.js
, add:
export const USER_LOADING = 'USER_LOADING';
export const USER_LOADED = 'USER_LOADED';
export const AUTH_ERROR = 'AUTH_ERROR';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAIL = 'LOGIN_FAIL';
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
export const REGISTER_SUCCESS = 'REGISTER_SUCCESS';
export const REGISTER_FAIL = 'REGISTER_FAIL';
npm install redux react-redux
npm install axios
npm install react-router-dom
npm install redux-devtools-extension
npm install redux-thunk
In Reducer/auth.js
, add:
import {
USER_LOADED,
USER_LOADING,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
REGISTER_FAIL
} from '../Actions/types';
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
isLoading: false,
user: null,
};
function AuthReducer (state = initialState, action) {
switch (action.type) {
case USER_LOADING:
return {...state,isLoading: true};
case USER_LOADED:
return {...state, isAuthenticated: true, isLoading: false, user: action.payload,};
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', action.payload.token);
return {...state, ...action.payload, isAuthenticated: true,isLoading: false,};
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT_SUCCESS:
case REGISTER_FAIL:
localStorage.removeItem('token');
return {...state, token: null, user: null, isAuthenticated: false, isLoading: false,};
default:
return state;
}
}
export default AuthReducer;
In Actions/auth.js
, add:
import axios from 'axios';
import {
USER_LOADED,
USER_LOADING,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
REGISTER_FAIL,
} from './types';
// CHECK TOKEN & LOAD USER
export const loadUser = () => (dispatch, getState) => {
// User Loading
dispatch({ type: USER_LOADING });
const token = getState().AuthReducer.token;
const config = {
headers :{
'Content-Type': 'application/json',
}
}
if(token){
config.headers['Authorization']=`Token ${token}`
}
axios
.get('http://127.0.0.1:8000/api/user/', config)
.then((res) => {
dispatch({
type: USER_LOADED,
payload: res.data,
});
})
.catch((err) => {
dispatch({
type: AUTH_ERROR,
});
});
};
//Login
export const login = (username, password) => dispatch => {
const config = {
headers :{
'Content-Type': 'application/json',
}
}
//Request body
const body= JSON.stringify({ username, password })
axios
.post('http://127.0.0.1:8000/api/login/', body, config)
.then((res) => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
dispatch({
type: LOGIN_FAIL,
});
alert("Invalid username or password")
});
};
//Logout user
export const logout = () => (dispatch, getState) => {
axios
.post('http://127.0.0.1:8000/api/logout/', null, tokenConfig(getState))
.then((res) => {
dispatch({ type: 'CLEAR_LEADS' });
dispatch({
type: LOGOUT_SUCCESS,
});
})
.catch((err) => {
dispatch({
type: AUTH_ERROR,
});
});
}
// Setup config with token - helper function
export const tokenConfig = (getState) => {
// Get token from state
const token = getState().AuthReducer.token;
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// If token, add to headers config
if (token) {
config.headers['Authorization'] = `Token ${token}`;
}
return config;
};
// REGISTER USER
export const register = ({ username, password }) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// Request Body
const body = JSON.stringify({ username, password });
axios
.post('http://127.0.0.1:8000/api/register/', body, config)
.then((res) => {
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
dispatch({
type: REGISTER_FAIL,
});
});
};
In Reducer/index.js
, add:
import { combineReducers } from 'redux';
import AuthReducer from './auth';
const AllReducers = combineReducers({
AuthReducer,
});
export default AllReducers;
In store.js
, add:
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import AllReducers from "./Reducers";
const store = createStore(AllReducers, composeWithDevTools(applyMiddleware(thunk)));
export default store;
Make sure your original index.js
looks like this:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import store from "./store";
ReactDOM.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById("root")
);
Your Login page should now work! If you get CORS error, make sure to install pipenv install django-cors-headers
in your backend code and add the following in settings.py
:
INSTALLED_APPS = [
...
'corsheaders'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
]
ALLOWED_HOSTS = ['*']
CORS_ORIGIN_ALLOW_ALL = True
Now to allow the user to register on the browser, we need to make Register.js
file and add:
import React, { Component } from 'react'
import {connect} from "react-redux";
import PropTypes from 'prop-types';
import { register } from './Actions/auth.js';
class Register extends Component {
state = {
username: "",
email:'',
password: "",
password2: '',
}
static propTypes ={
register: PropTypes.func.isRequired,
}
onSubmit = e => {
e.preventDefault();
const { username, email, password, password2 } = this.state;
if (password !== password2) {
alert("Passwords do not match")
} else if (username == '' || email == '' || password==''){
alert("Please provide all the details")
} else {
const newUser = {
username,
password,
};
this.props.register(newUser);
e.target.reset();
alert("You have successfully registered!")
}
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<fieldset>
<legend>Register</legend>
<div>
<label>Username </label>
<input type="text" onChange={e => this.setState({username: e.target.value})} />
</div>
<div>
<label>Email </label>
<input type="text" onChange={e => this.setState({email: e.target.value})} />
</div>
<div>
<label>Password </label>
<input type="password" onChange={e => this.setState({password: e.target.value})} />
</div>
<div>
<label>Confirm Password </label>
<input type="password" onChange={e => this.setState({password2: e.target.value})} />
</div>
<button type="submit">Register</button>
</fieldset>
</form>
</div>
)
}
}
export default connect(null, { register })(Register);
After you create a /register
route in your App.js
, your register page should work.
Next, you need to make a PrivateRoute.js
to make use users can only access the /home
once they have successfully logged in!
In your PrivateRoute.js
file, add:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
const PrivateRoute = ({ component: Component, AuthReducer, ...rest }) => (
<Route
{...rest}
render={(props) => {
if (AuthReducer.isLoading) {
return <h2>Loading...</h2>;
} else if (!AuthReducer.isAuthenticated) {
return <Redirect to="/login" />;
} else {
return <Component {...props} />;
}
}}
/>
);
const mapStateToProps = (state) => ({
AuthReducer: state.AuthReducer,
});
export default connect(mapStateToProps)(PrivateRoute);
Make sure your App.js
looks like this:
import Login from './Login.js'
import Home from './Home.js'
import Register from './Register.js';
import PrivateRoute from './PrivateRoute.js'
function App() {
return (
<>
<Switch>
<PrivateRoute path="/home" component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
</Switch>
</>
);
}
export default App;
Now, if you try to go to /home
page without logging in first, it will redirect you to /login
page.
Lastly, we need a log out button!
Create a Logout.js
file and add:
import React, { Component } from 'react'
import {connect} from "react-redux";
import PropTypes from 'prop-types';
import { logout } from './Actions/auth.js'
class Logout extends Component {
static propTypes ={
AuthReducer: PropTypes.object.isRequired,
logout: PropTypes.func.isRequired
}
render() {
const { isAuthenticated } = this.props.AuthReducer
return (
<div>
{isAuthenticated ? <button onClick={this.props.logout}>Logout</button> : <h1></h1>}
</div>
)
}
}
const mSTP = (state) =>({
AuthReducer: state.AuthReducer
})
export default connect(mSTP, { logout })(Logout)
Bring this Logout
button to your Home
file, like this:
import React, { Component } from 'react';
import Logout from './Logout.js';
class Home extends Component {
render() {
return (
<div>
<Logout/>
Welcome
</div>
)
}
}
export default Home;
Top comments (0)