Search code examples
reactjsreduxreact-reduxredux-formreact-redux-form

reduxForm not submitting , gives error Unhandled Rejection (SubmissionError): Submit Validation Failed


I'm using reduxForm 7.4.2 , I want to submit form and display server side validation error , but it gives me below error :

enter image description here

Server response :

enter image description here

Here is LoginForm component :

import React,{Component} from 'react'
import InputGroup from '../ui/InputGroup';
import { bindActionCreators } from "redux"
import { Field , reduxForm,SubmissionError} from 'redux-form';
import {faEnvelope,faLock} from '@fortawesome/free-solid-svg-icons'
import { connect } from 'react-redux';
import { loginUser,loginUserFailure,loginUserSuccess } from '../../actions/authActions';


class LoginForm extends Component {
    constructor(){
        super();
        this.submitLoginForm=this.submitLoginForm.bind(this);
    }

    submitLoginForm=values=>{         
       this.props.loginUser(values);      
    }

    render() {
    const { handleSubmit, submitting} = this.props;
    return(
        <form onSubmit={handleSubmit(this.submitLoginForm)}>
                    <Field component={InputGroup} type="email" placeholder="Email" id="email" name="email" icon={faEnvelope}></Field>
                    <Field component={InputGroup} type="password" placeholder="Password" id="password" name="password" icon={faLock}></Field>
                    <button type="submit" className="btn btn-success btn-block" disabled={submitting}>Login</button>
        </form>
    )
  }
}

const mapStateToProps = (state, ownProps) => ({
    user:state.user
})


 const mapDispatchToProps = (dispatch) => {
   return bindActionCreators({
    loginUser,
    loginUserFailure,
    loginUserSuccess,
    }, dispatch)
  }

LoginForm = connect(mapStateToProps,mapDispatchToProps)(LoginForm);
LoginForm = reduxForm({ form: 'loginForm'})(LoginForm);
export default LoginForm;

src/actions/authActions :

import axios from 'axios';
import { SubmissionError} from 'redux-form';


export const LOGIN_USER = 'LOGIN_USER';
export const LOGIN_USER_SUCCESS = 'LOGIN_USER_SUCCESS';
export const LOGIN_USER_FAILURE = 'LOGIN_USER_FAILURE';

export const loginUser =userdata=>{
   return dispatch => {
    try {
      const request = axios.post("/api/auth/login", userdata);
      request
        .then(res => {
          dispatch(loginUserSuccess(request));
        })
        .catch(e => {
          //dispatch(loginUserFailure(e));
          dispatch(loginUserFailure(e.response.data));
          throw new SubmissionError(e.response.data);
        });
    } catch (e) {
      dispatch(loginUserFailure(e));
    }
  }; 
 }

export const loginUserSuccess=user=>{
    return {
        type:LOGIN_USER_SUCCESS,
        payload:user
    }
}

export const loginUserFailure=error=>{
    return {
        type:LOGIN_USER_FAILURE,
        payload:error
    }
}

src/reducers/authReducer.js :

import { LOGIN_USER,LOGIN_USER_SUCCESS,LOGIN_USER_FAILURE} from './../actions/authActions';

const INITIAL_STATE = {isAuthenticated: false,user: null, status:null, error:null, loading: false};

  export default function(state = INITIAL_STATE, action) {
    let error;
    switch (action.type) {
        case LOGIN_USER:
        return { ...state, isAuthenticated: false,user: null, status:'login', error:null, loading: true}; 

        case LOGIN_USER_SUCCESS:
        return { ...state, isAuthenticated: true,user: action.payload.user, status:'authenticated', error:null, loading: false}; 

        case LOGIN_USER_FAILURE:
        error = action.payload.data || {message: action.payload.message};     
        return { ...state,isAuthenticated: false, user:null, status:'login', error:error, loading: false};

  default:
        return state;
    }
}; 

store.js

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const initialState = {};
const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  compose(
    applyMiddleware(...middleware),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
);
export default store;

root reducer

import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form'
import authReducer from './authReducer';

export default combineReducers({
    form: formReducer,
    auth:authReducer
});

Solution

  • There are two things here which you are doing incorrectly. Maybe I am missing other issues, but I am sure about these two:

    Bind your action creators in dispatch to be able to dispatch the action in dumbcomponent Drawer. Do it something like this(only changed part, I am editing):

    import { bindActionCreators } from "redux"
    const mapDispatchToProps = (dispatch) => {
      return bindActionCreators({
        loginUser,
        loginUserFailure,
        loginUserSuccess,
      }, dispatch)
    }
    

    You must be using redux-thunk as middleware. It is used to handle asynchronous dispatches when using redux with react. Your api call is asynchronous, so you cannot directly return the response after making axios post call. you need to return a function there, something this way:

    export const loginUser = userdata => {
          return dispatch => {
            try {
              const request = axios.post("/api/auth/login", userdata);
              return request
                .then(res => {
                  dispatch(loginUserSuccess(request));
                })
                .catch(e => {
                  dispatch(loginUserFailure(e));
                });
            } catch (e) {
              dispatch(loginUserFailure(e));
            }
          };
        };
    

    And remove the promise handling in your dumbcomponent. because all your data is now available in redux, which you can get through mapStateToProps.

    Note: I may have not taken care of curly braces. Please check that and feedbacks are welcome