Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Redux Axios: POST Request not receiving credentials from redux state

I have been working on authentication with my project. I have a REST api backend that serves JWT tokens. My front end stack is ReactJS, Redux, Axios and Redux Thunk.

My question is why when I submit my form it does not send any credentials?

But when I console log the action and payload on credChange it seems to be correct. Am I not setting the state somewhere? Also, axios does not catch the 400 Bad Request error.

Here is my code:

AuthActions.js

export const credChange = ({ prop, value }) => {
  return {
    type: CRED_CHANGE,
    payload: { prop, value },
  };
};
export const logoutUser = () => {
  return (dispatch) => {
    dispatch({ type: LOGOUT_USER });
  };
};
const loginSuccess = (dispatch, response) => {
  dispatch({
    type: LOGIN_USER_SUCCESS,
    payload: response.data.token,
  });
};
const loginError = (dispatch, error) => {
  dispatch({
    type: LOGIN_USER_ERROR,
    payload: error.response.data,
  });
};
export const loginUser = ({ empNum, password }) => {
  return (dispatch) => {
    dispatch({ type: LOGIN_USER });
    axios({
      method: 'post',
      url: 'http://127.0.0.1:8000/profiles_api/jwt/authTK/',
      data: {
        emp_number: empNum,
        password,
      },
    })
      .then(response => loginSuccess(dispatch, response))
      .catch(error => loginError(dispatch, error));
  };
};

AuthReducer.js

const INITIAL_STATE = {
  empNum: '',
  password: '',
  empNumErr: null,
  passwordErr: null,
  authTK: null,
  loading: false,
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case CRED_CHANGE:
      return { ...state, [action.payload.prop]: action.payload.value };
    case LOGIN_USER:
      return {
        ...state,
        ...INITIAL_STATE,
        loading: true,
      };
    case LOGOUT_USER:
      return {
        ...state,
        INITIAL_STATE,
      };
    case LOGIN_USER_SUCCESS:
      return {
        ...state,
        ...INITIAL_STATE,
        authTK: action.payload,
      };
    case LOGIN_USER_ERROR:
      return {
        ...state,
        ...INITIAL_STATE,
        empNumErr: action.payload.emp_number,
        passwordErr: action.payload.password,
      };
    default:
      return state;
  }
};

LoginForm.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

import {
  credChange,
  loginUser,
  logoutUser,
} from '../Actions';

class LoginForm extends Component {
  constructor() {
    super();
    this.onFormSubmit = this.onFormSubmit.bind(this);
    this.renderEmpNumErr = this.renderEmpNumErr.bind(this);
    this.empNumChange = this.empNumChange.bind(this);
    this.passwordChange = this.passwordChange.bind(this);
  }
  onFormSubmit() {
    const { empNum, password } = this.props;
    this.props.loginUser({ empNum, password });
  }
  empNumChange(text) {
    this.props.credChange({ prop: 'empNum', value: text.target.value });
  }
  passwordChange(text) {
    this.props.credChange({ prop: 'password', value: text.target.value });
  }
  renderEmpNumErr() {
    if (this.props.empNumErr) {
      return (
        <p>
          {this.props.empNumErr}
        </p>
      );
    }
    return null;
  }
  render() {
    return (
      <div>
        <form onSubmit={this.onFormSubmit}>
          <label htmlFor="numberLabel">Employee Number</label>
          <input
            id="numberLabel"
            type="password"
            value={this.props.empNum}
            onChange={this.empNumChange}
          />
          <label htmlFor="passLabel">Password</label>
          <input
            id="passLabel"
            type="password"
            value={this.props.password}
            onChange={this.passwordChange}
          />
          <button type="submit">Login</button>
        </form>
        {this.renderEmpNumErr()}
      </div>
    );
  }
}

const mapStateToProps = ({ counter }) => {
  const {
    empNum,
    password,
    loading,
    empNumErr,
    passwordErr,
    authTK,
  } = counter;
  return {
    empNum,
    password,
    loading,
    empNumErr,
    passwordErr,
    authTK,
  };
};

export default connect(mapStateToProps, { credChange, loginUser, logoutUser })(LoginForm);

After Submitting form with credentials

The console says:

POST XHR http://127.0.0.1:8000/profiles_api/jwt/authTK/ [HTTP/1.0 400 Bad Request 5ms]

And the POST request Raw Data is blank, therefore no credentials were sent.

{"emp_number":["This field is required."],"password":["This field is required."]}

EDIT If there is any other information I can provide please say so but I think this should be sufficient.

like image 949
Justin Smith Avatar asked Nov 06 '17 22:11

Justin Smith


1 Answers

Looks like empNum and password aren't getting set in the state. This is because the action object returned by credChange doesn't get dispatched, so the reducer never get called:

// dispatch calls the reducer which updates the state
dispatch(actionCreator())

// returns an action object, doesn't call reducer
actionCreator() 

You can dispatch actions automatically by calling a bound action creator:

// calls the reducer, updates the state
const boundActionCreator = () => {dispatch(actionCreator())}

// call boundActionCreator in your component
boundActionCreator()

mapDispatchToProps can be used to define bound action creators (to be passed as props):

const mapDispatchToProps = (dispatch) => {

  return {
      credChange: ({ prop, value }) => {dispatch(credChange({prop, value})},
      loginUser: ({ empNum, password }) => {dispatch(loginUser({empNum, password})},
      logoutUser: () => {dispatch(logoutUser()},
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);

This should solve the state update issue, allowing props that read from state (empNumber, password, etc.) to update as well.

like image 64
Grayson Langford Avatar answered Oct 31 '22 22:10

Grayson Langford