Valentin
Valentin

Reputation: 191

Redux and Token Authentication : how to redirect the user

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

Answers (1)

Melchia
Melchia

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

Related Questions