5

I am learning React Redux with Create-React-App

I am having trouble with the logic of stores, reducers, actions ect.

I have a simple login page (some JSX omitted to make it simpler to read)

Login.js

import React, { Component } from "react";
import { connect } from "react-redux";
import * as actions from '../../actions';
import Logo from "../img/image-center.png";
import "./login.css";


class Login extends Component {
  constructor(props){
    super(props);
    this.state = {
      errorMsg : ""
    };
    this.loginClicked = this.loginClicked.bind(this);   
    console.log("loggedIn:", this.props.login);
  }

  loginClicked() {
    try {
      this.props.loginUser(this.refs.email.value, this.refs.password.value)
      .then(() => {
        console.log("thisprops", this.props);        
      });
    }
    catch(ex){
      this.state.errorMsg = "Unable to connect to server";
      console.log("error", ex);
    }
  }

  render() {
    return (

      <div className="login-bg">
        <div className="container signForm">
            <div className="col s12 l6 login-form">
              <p className="center-align">Login to Trade Portal</p>
              <form>
                <div className="row">
                  <div className="input-field">
                    <input id="email" type="email" className="validate" ref="email" />
                    <label htmlFor="email">Email</label>
                  </div>
                </div>
                <div className="row">
                  <div className="input-field">
                    <input id="password" type="password" className="validate" ref="password" />
                    <label htmlFor="password">Password</label>
                  </div>
                </div>
                <div className="row">
                  <button
                    className="btn waves-effect waves-light"
                    type="button"
                    name="action"
                    onClick={this.loginClicked}
                    >
                    Submit
                  </button>
                  <a href="/subcontractor" className="register">
                    Register here
                  </a>
                </div>
                <div className="row"><div style={{textAlign: "center", color:"red"}}>{this.props.login.message}</div></div>   

              </form>
            </div>
          </div>
        </div>
      </div>
    );
  }
}


function mapStateToProps({login}){
  return { login } ;
}

export default connect(mapStateToProps, actions)(Login);

As you can see the LoginClick fucntion calls a prop loginUser which is derived using connect (and actually calls my Action Login method)

actions\index.js

import axios from "axios";
import { FETCH_USER, LOGIN_USER, LOGIN_FAILED } from "./types";


export const fetchUser = () => async dispatch => {
  const res = await axios.get("/api/Account/GetUser");
  dispatch({ type: FETCH_USER, payload: res.data });
};

export const loginUser = (username, password) => async dispatch => {
    try {
        const res = await axios.post("/api/Account/Login", {username: username, password: password, persistant: false });
        const message = res.data ? "" : "Incorrect username or password";
        dispatch({ type: LOGIN_USER, payload: { success: res.data, message: message } });
    }
    catch(ex){
        console.log("Login Failed:", ex);
        dispatch({ type: LOGIN_FAILED, payload: { success: false, message: "Unable to connect to authentication server" } });
    }
}

The above code calls my Server and logs in the user, when succesful it is read in the loginReducer.js

import { LOGIN_USER, LOGIN_FAILED } from "../actions/types";

export default function(state = null, action) {
  console.log(action);
  switch (action.type) {
    case LOGIN_USER:      
      return { success: action.payload.success, message: action.payload.message };
      break;
    case LOGIN_FAILED:
      return { success: false, message: action.payload.message };
      break;
      default:
    return { success: false, message: ""};
  }
}

Now my question is where and how do I redirect the user on succeful Logging in to the home page? "/Home"

I am hoping I can do it in Login.js somewhere, as one day I may want to have 2 login routes (Ie If logging in from a header it may not redirect to home, but when logging in from Login.js it should log into home). Therefore I dont think this action should go in the Action or Reducer. But I am unsure how I can bind it in Login.js so when Login successfull, redirect

michael
  • 6,405
  • 14
  • 52
  • 98
  • i would do this in a saga. checkout redux-saga, i'm really glad i took that approach when learning redux. edit: looks like you tagged thunks, you could do this in a thunk as well. – brub Jan 17 '18 at 06:04
  • yes tagged thunk because I use thunk.. but im mostly confused how I bind an action in my login.js file to redirect the user to the home page.. after the dispatch. I can bind messages in JSX easily, so that if I update the store, it updates the message, but how can I bind a function so when the login state updates to logged in, it redirects if on that page.. – michael Jan 17 '18 at 06:07

3 Answers3

4

Possible ways-

1- Redirect from loginClicked method after successful login (inside .then), Like this:

loginClicked() {
    try {
      this.props.loginUser(this.refs.email.value, this.refs.password.value)
      .then(() => {
          this.props.history.push('/home');       
      });
    }
    catch(ex){
      this.state.errorMsg = "Unable to connect to server";
      console.log("error", ex);
    }
}

2- Or another option is put the check inside render method, whenever any change happens to store, it will re-render the component and if you find the success == true redirect to home page, Like this:

render(){

    if(this.props.success)
        return <Redirect to="/home" />

    return (....)
}

If you follow the first approach then you need to put the check inside componentDidMount method also, if user want to open login page after successful login, redirect to Home page. You are maintaining the login session by bool success check that bool value.

componentDidMount(){
   if(this.props.success) {
      this.props.history.push('/home');
   }
}

Suggestion:

Avoid using string refs, as per DOC:

If you worked with React before, you might be familiar with an older API where the ref attribute is a string, like "textInput", and the DOM node is accessed as this.refs.textInput. We advise against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases. If you’re currently using this.refs.textInput to access refs, we recommend the callback pattern instead.

Mayank Shukla
  • 80,295
  • 14
  • 134
  • 129
0

Redux-thunk will return out whatever you return from your thunk, meaning if you return a promise (which your are already doing because it's an async function), you can simply await it in Login.js and then perform the redirect.

Looks like you could simply modify console.log("thisprops", this.props); in Login.js that takes place after loginUser is called to perform the redirect.

casieber
  • 5,595
  • 1
  • 14
  • 30
0

You can do it in the react lifecycle.

componentWillReceiveProps(nextProps) {
  if (nextProps.login.success && nexProps.login.success !== this.props.login.success) {
     this.props.history.push('/home');
  }
 }
soupette
  • 1,130
  • 9
  • 11