Reputation: 191
I made my own Django rest Framework API and I included token authentication. Everything works fine, I can get tokens and stuff, BUT, I have some trouble redirecting my user, using :
this.props.push('/');
Here is the Logic behind the authentication for the UI :
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {authLogin} from '../../actions/authentication.js';
class Login extends Component {
constructor(props){
super(props);
this.state = {
email: "",
password: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e){
this.setState({[e.target.name]: e.target.value});
}
handleSubmit(e){
e.preventDefault();
this.props.authLogin(this.state.email, this.state.password);
console.log(this.props.isAuthenticated);
console.log(this.props.loading);
if (this.props.isAuthenticated){
this.props.history.push('/');
}
}
render(){
return(
<div className="Login">
<h1> This is a form </h1>
<form>
<input type="text" onChange={this.handleChange} name="email"/>
<input type="password" onChange={this.handleChange} name="password"/>
<input type="submit" onClick={this.handleSubmit}/>
{ this.props.isAuthenticated &&
<p>Hello </p>
}
</form>
</div>
)
}
}
const mapStateToProps = state => {
console.log(state);
return {
token: state.authentication.token
}
}
export default connect(mapStateToProps, {authLogin})(Login);
This Login Component is inside a Container Component and this is where I want my Redux to tell " hey, here is the new token " :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {connect } from 'react-redux';
import AppHeader from './header/AppHeader';
import HomePageStream from './HomePageStream/HomePageStream.js';
import HeaderCategories from './header/HeaderCategories.js';
import ArticleDetails from './ArticleDetails/ArticleDetails.js';
import { Route, Switch } from 'react-router-dom';
import { transitions, positions, Provider as AlertProvider } from 'react-alert'
import AlertTemplate from 'react-alert-template-basic'
import Alerts from './Alerts/Alerts.js';
import {authStateCheck} from '../actions/authentication.js';
import { Provider } from 'react-redux'
import { store } from '../store.js';
import { BrowserRouter } from "react-router-dom";
import Login from './Authentication/Login.js';
const options = {
timeout: 2000,
position: "top center"
}
class Container extends Component {
componentDidMount(){
this.props.authStateCheck();
}
render(){
return(
<div className="Container">
<Provider store={store}>
<AlertProvider template={AlertTemplate}
{...options}
>
<AppHeader />
<Alerts />
<HeaderCategories />
<Switch>
<Route exact
path="/:category"
render={routerProps => <HomePageStream {...routerProps}/>}
/>
<Route exact
path="/article/:slug"
render={routerProps => <ArticleDetails {...routerProps}/>}
/>
<Route exact
path="/authentication/login"
render={ routerProps => <Login {...routerProps}{...this.props}/>}
/>
</Switch>
</AlertProvider>
</Provider >
</div>
)
}
}
const mapStateToProps = state => {
console.log(state.authentication);
return {
isAuthenticated: state.authentication.token !== null,
loading: state.authentication.loading
}
}
export default connect(mapStateToProps, {authStateCheck} )(Container);
Here is the Problem : When I click on submit, isAuthenticated is false, because I only get the token AFTER handleSubmit() has been called !
Here is the code for my action :
import axios from 'axios';
import {AUTH_START, AUTH_FAIL, AUTH_SUCESS, AUTH_LOGOUT} from './type';
export const authStart = () => {
return {
type: AUTH_START,
loading: true
};
}
export const authSucess = token => {
console.log(token);
return {
type: AUTH_SUCESS,
token: token,
error: null,
loading: false
};
}
export const authFail = error => {
console.log(error)
return {
token: null,
type: AUTH_FAIL,
error: error,
loading: false
};
}
export const logout = () => {
window.localStorage.removeItem('token');
window.localStorage.removeItem('expiration_time');
return {
type: AUTH_LOGOUT
}
}
export const checkAuthTimeOut = expiration_time => dispatch => {
return setTimeout(()=> {
dispatch(logout());
}, expiration_time * 1000);
}
export const authLogin = (email, password) => dispatch => {
dispatch(authStart());
console.log("I'm authlogin ! ");
axios.post('http://127.0.0.1:8000/rest-auth/login/',{
"email": email,
"password": password
})
.then( res => {
console.log("RESPONSE !")
const token = res.data.key
const expiration_time = new Date(new Date().getTime() + 3600 * 1000);
window.localStorage.setItem('token', token);
window.localStorage.setItem('expiration_time', expiration_time);
dispatch(authSucess(token));
console.log(token);
dispatch(checkAuthTimeOut(3600));
})
.catch( err => {
console.log(err);
dispatch(authFail());
})
}
export const authStateCheck = () => dispatch => {
const expiration_time = window.localStorage.getItem('expiration_time');
const token = window.localStorage.getItem('token');
if (!token){
return dispatch(logout());
} else if ( expiration_time <= new Date()){
console.log("EXPIRATION");
console.log( expiration_time <= new Date() )
return dispatch(logout());
} else {
console.log("NEW TIMER !!!! ");
return checkAuthTimeOut((expiration_time - new Date().getTime()) / 1000)
}
}
Here is my reducer :
import {AUTH_START, AUTH_FAIL, AUTH_SUCESS, AUTH_LOGOUT} from '../../actions/type';
const initialState = {
error: null,
token: null,
loading: false
}
export function authenticationReducer(state = initialState, action){
switch (action.type) {
case AUTH_START:
return {
...state,
loading: true
}
case AUTH_SUCESS:
console.log(action.token);
console.log(action.loading);
return {
...state,
error: null,
token: action.token,
loading: false
}
case AUTH_FAIL:
return {
...state,
error: action.error
}
default:
return state
}
}
But if i hit submit one more time, it works. But i really want my user to be immediately redirected. How can I fix this ??
B Thank you so much ;(
Upvotes: 2
Views: 862
Reputation: 24234
I would suggest removing the history.push code and let react-router handle the redirection:
import { Route, Redirect } from 'react-router'
...
<Switch>
<Route exact
path="/:category"
render={routerProps => <HomePageStream {...routerProps}/>}
/>
<Route exact
path="/article/:slug"
render={routerProps => <ArticleDetails {...routerProps}/>}
/>
<Route exact
path="/authentication/login"
render={ routerProps => (this.props.isAuthenticated ? (<Redirect to="/"/>) :(<Login {...routerProps}{...this.props}/>))}
/>
</Switch>
Upvotes: 2