Search code examples
reactjsvalidationasynchronousaxiosredux-form

Async Validation Redux Form Error Uncaught promise


I am trying to build a signup form using redux-form. I am trying to asynchronously validate if an email have been already taken.

If I enter a taken email, the input box becomes invalid (as expected), but when I enter an unique email and try to submit I run into the following error in console: "Uncaught (in promise) {email: "This email has already been taken"}"

Thanks!

//asyncValidate.js
import axios from 'axios';
const asyncValidate = (values)=>
{
    return axios.post('/api/emails',{email:values.email})
    .then(res => {
      if (res) {
        throw { email: "This email has already been taken."};
      }
    })
}
export default asyncValidate;

//route.js
app.post('/api/emails', (req, res)=>{
    User.findOne({email: req.body.email},(err,user)=>{
    if (err) {console.log("error");}
    else  {res.json(user)};
});
});

//SignUpForm.js
import {Grid } from 'semantic-ui-react';
import AccountFields from './AccountFields';
import ContactFields from './ContactFields';
import HistoryFields from './HistoryFields';
import WaiverFields from './WaiverFields';
import * as actions from '../../actions';
import { withRouter } from 'react-router-dom';

 class SignUpForm extends Component {
    constructor(props){
    super(props)
    this.nextPage = this.nextPage.bind(this)
    this.previousPage = this.previousPage.bind(this)
    this.state ={
        page:1
    }

}

nextPage(){
this.setState({ page:this.state.page + 1})
}

previousPage(){
this.setState({ page: this.state.page - 1 })
}


render(){
    const { onSubmit,submitForm, history } = this.props
    const { page } = this.state
    return(
        <div>
      <Grid textAlign='center' style={{ height: '1000px' }} verticalAlign='middle'>


            <Grid.Column style={{ maxWidth: 700 }} textAlign='left'>
                {page === 1 && <AccountFields onSubmit={this.nextPage}/>}
                        {page === 2 && (
                          <ContactFields
                            previousPage={this.previousPage}
                            onSubmit={this.nextPage}
                          />
                        )}
                        {page === 3 && (
                          <HistoryFields
                            previousPage={this.previousPage}
                            onSubmit={this.nextPage}
                          />
                        )}
                        {page === 4 && (
                            <WaiverFields
                                previousPage={this.previousPage}
                                onSubmit={(v)=>submitForm(v,history)}
                                />
                        )}

</Grid.Column>
</Grid>
</div>
export default connect(null,actions)(withRouter(SignUpForm));



//AccountFields.js
class AccountFields extends Component {

render(){
    const { handleSubmit} = this.props
    return(



<Form size='large' onSubmit={handleSubmit}>


  <Segment stacked>







      <Header as='h1' color='black' textAlign='left'>
          <Image src={icon1} />
      <Header.Content>
       Create your Account
       <Header.Subheader>     to make an online appointment</Header.Subheader>
      </Header.Content>
      </Header>



<Field
    name='email'
    label='E-mail'
    component={renderField}
    as={Form.Input}
    type='email'
    icon='user'
    iconPosition='left'
    placeholder='E-mail Address'
    />



      <Form.Group widths='2'>
          <Field
              name='password'
              label='Password'
              component={renderField}
              as={Form.Input}
              type='password'
              icon='lock'iconPosition='left'
              placeholder='Password'/>

              <Field
                  name='password1'
                  label='Confirm Password'
                  icon="lock" iconPosition='left'
                  component={renderField}
                  as={Form.Input}
                  type='password'

                  placeholder='Confirm Password'
                  />


      </Form.Group>
      <Button type='submit'style={{marginTop:'5px'}} color='black' floated='right'  compact size='large'>
            Next
          </Button>
          <Link to='/login' >
<Button style={{marginTop:'5px'}}color='black' basic floated='left'>Log In Instead </Button>
</Link>


    <br></br>
    <br></br>

  </Segment>
</Form>)}}



export default reduxForm({
  form: 'wizard', // <------ same form name
  destroyOnUnmount: false, // <------ preserve form data
  forceUnregisterOnUnmount: true, // <------ unregister fields on unmount
  validate,
  asyncValidate,
  asyncBlurFields: ['email']

})(AccountFields)

Solution

  • Even though you don't specify a response if your DB call doesn't find a user, the POST request will still receive a response from your route, though it'll probably be an error message saying that there was no response. In either case, your .then block will receive the response and so throw the error.

    What you should do instead is have your POST handler return either an all-OK response if no user is found or an error message if the user already exists. If you use a 400 block HTTP response code if the user exists and a 200 if they don't then the error will go into a .catch block on your axios request and you can handle it there, whereas a success will go into the .then and you can do what you want there.