clusterBuddy
clusterBuddy

Reputation: 1554

Form submit only submits on the 2nd click, need callback in ES6

Clicking on Submit invokes my onSubmit function where it does 2 things:

  1. setState for formValidity (bool)
  2. submits the form

The issue is that it fails on first click and only submits on the second time. I understand this is coming from eval that submits even though it doesn't yet have the states set and fails on first run.

I would like onSubmit to preform setStates and only then preform eval for sending the mail in one click.

Please note: I've tried splitting the function and that had the same result.

I'm assuming a callback would be in place here yet I don't know how to apply this correctly with ES6.

rendered element:

<button onClick={(event) => this.onSubmit(event)}>Submit</button>

onSubmit function:

onSubmit = (event) => {
    event.preventDefault();
    if (!validator.isEmail(this.state.email)) {
        this.setState({
            errorType: this.state.formErrors["email"],
            formValidity: false
        })
    } else {
        this.setState({
            formValidity: true
        })
    }
    if (!validator.isAlpha(this.state.fName) || !validator.isAlpha(this.state.lName)) {
        this.setState({
            errorType: this.state.formErrors["name"],
            formValidity: false
        })
    } else {
        this.setState({
            formValidity: true
        })
    }
    if (this.state.formValidity === true) {
        console.log('mail sent!');

    } else {
        console.log('One of the inputs is erroneous');

    }

}

Just to iterate to make sure I explained this well, I would like if (this.state.formValidity === true) to run **only after ** all the other conditionals eval'd and stay in the same function scope.

thanks, Bud

Upvotes: 0

Views: 112

Answers (3)

Kox
Kox

Reputation: 853

Remove the event handler from the button and add it to your form element:
<form onSubmit={this.handleOnSubmit}> and bind this.handleOnSubmit in the constructor.

Regarding your checkups, I would advise you to perform your check for each of the input fields upon their change event and update the state accordingly. For example:

<input name="email" onChange={this.validateEmail}>

And this (binded) function can perform your check for the email field, if it’s valid and if the name field is valid, simply update the this.formValidity state to true.

Then, in your handleOnSubmit function, remove everything except for your form validation checkup.

EDIT:
Change all your inputs' onChange handlers to contain a reference to a specific function that will perform a validation for certain input field (and update the corresponding field's value - if you are already using that). For example (referring to the input field from before):

validateEmail() {
   // Update input's state value
   // Perform validation for email input and update corresponding state
}

Now the thing is, I would advise you to create additional state properties (one for each input field) to track individual field's validity state, like so:

this.state = {
  ...,
  fNameValid: false,
  lNameValid: false,
  emailValid: false,
  telValid: false,
}

Now your specific validation functions will only update the state that is concerned with the specific field, not the whole form (and your errorType):

validateEmail() {
   // Update input's state value

   if (!validator.isEmail(this.state.email)) {
      this.setState({
        emailValid: false,
        errorType: this.state.formErrors["email"],
      })
   } else {
      this.setState({
        emailValid: true
      })
   }
}

Finally, have your onSubmit event handler perform the checkup for all fields:

handleOnSubmit() {
   let isFormValid = this.state.fNameValid
                  && this.state.lNameValid
                  && this.state.emailValid
                  && this.state.telValid;

   if (isFormValid) {
     console.log('mail sent!');
   } else {
     console.log('One of the inputs is erroneous');
   }
}

State's formValidity property becomes redundant in the end.

Upvotes: 2

hbauer
hbauer

Reputation: 114

Do you need to test formValidity's value from state? If not, if (this.state.formValidity === true) is overkill, and you can restructure your logic so that the async nature of setState is easier to account for — something like:

onSubmit = event => {
  event.preventDefault()

  let formValidity = true
  let errorType = undefined

  if (!validator.isEmail(this.state.email)) {
    formValidity = false
    errorType = this.state.formErrors.email
  } else if (!validator.isAlpha(this.state.fName) || !validator.isAlpha(this.state.lName)) {
    formValidity = false
    errorType = this.state.formErrors.name
  }

  if (!formValidity) {
    this.setState({
      formValidity,
      errorType
    }, () => console.log('Form is invalid'))
  } else {
    this.setState({
      formValidity,
      errorType
    }, () => console.log('Form is valid'))
  }
}

You'll also see I take advantage of setState's callback, which can be safely assumed to run only after the state has been set.

Upvotes: 1

anuragb26
anuragb26

Reputation: 1475

setState is an async function,you could add the mail sent code or any such code dependent on formValidity=true as a callback to the setState function so there should be no need of the last if-else block.Always avoid the last if-else block since setState runs in an async manner.

this.setState({
        formValidity: true
    },function(){
        console.log("mail sent");
         //or any other code dependent on formValidity set to true.
    });

Upvotes: 0

Related Questions