Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v6: Form Submit : startSubmit action delayed due to asyncValidate #888

Closed
devarsh opened this issue Apr 30, 2016 · 11 comments
Closed

v6: Form Submit : startSubmit action delayed due to asyncValidate #888

devarsh opened this issue Apr 30, 2016 · 11 comments
Milestone

Comments

@devarsh
Copy link

devarsh commented Apr 30, 2016

@erikras
During form submission, we fire up asyncValidate and when the async promise is resolved it calls the doSubmit action with in turn calls the startSubmit, due to which the submitting props received to the form is trigged lately, can we fire up the startSubmit as soon as the forms submitHandler is called.

const handleSubmit = (submit, props, valid, asyncValidate, fields) => {
  const { dispatch, startSubmit, stopSubmit, setSubmitFailed, syncErrors,
    returnRejectedSubmitPromise, values } = props

  if (valid) {
    const doSubmit = () => {
      const result = submit(values, dispatch)
      if (isPromise(result)) {
        startSubmit()
        return result
          .then(submitResult => {
            stopSubmit()
            return submitResult
          }).catch(submitError => {
            stopSubmit(submitError instanceof SubmissionError ? submitError.errors : undefined)
            if (returnRejectedSubmitPromise) {
              return Promise.reject(submitError)
            }
          })
      }
      return result
    }
    const asyncValidateResult =  asyncValidate && asyncValidate()
    if(asyncValidateResult) {
      return asyncValidateResult
        .then(
          doSubmit,
          asyncErrors => {
            setSubmitFailed(...fields)
            if (returnRejectedSubmitPromise) {
              return Promise.reject(asyncErrors)
            }
          })
    } else {
      return doSubmit()
    }
  } else {
    setSubmitFailed(...fields)

    if (returnRejectedSubmitPromise) {
      return Promise.reject(syncErrors)
    }
  }
}
@devarsh devarsh changed the title V6: Form Submit : startSubmit action delayed due to asyncValidate v6: Form Submit : startSubmit action delayed due to asyncValidate Apr 30, 2016
@erikras
Copy link
Member

erikras commented Apr 30, 2016

I'm sorry, what's the question? You think that submitting should be set as soon as we enter handleSubmit() and not after async validation?

@devarsh
Copy link
Author

devarsh commented Apr 30, 2016

Yes, because if we look at async validation example, the submit button when clicked its not disabling the submit button immediately, and giving user a chance to submit more than once.

@mihirsoni
Copy link
Contributor

@erikras Yes i do agree , allowing multiple submit to user will be wrong thing to do

Screencapture GIF

@mihirsoni
Copy link
Contributor

Duplicates #829

@devarsh
Copy link
Author

devarsh commented Apr 30, 2016

I think this will do the trick <button type="submit" disabled={submitting || asyncValidating }>Submit</button> maybe just add it to async examples.

I'm bit confused as to why is asyncValidation being called on formSubmit again, since its called on each async validating element's onBlur event.

And if a user submits the form while async validation is in process, why is the form allowed to be submitted, can we not make form invalid so that from submit action will have no effect, since handleSubmit function receives valid as a props

@ooflorent ooflorent added this to the next-6.0.0 milestone Apr 30, 2016
@erikras
Copy link
Member

erikras commented May 1, 2016

I think this will do the trick <button type="submit" disabled={submitting || asyncValidating }>Submit</button> maybe just add it to async examples.

This is the correct answer.

To answer your other questions, handleSubmit cannot be 100% sure that async validation has been run and has completed, so it must run it again itself. If you're just clicked Submit coming from an async validated input, async validation will be running, but there is no way (that I know of) for handleSubmit to block until the original async validation call is complete. Perhaps the async promise could be stored locally on this or something...? It will require some investigation.

@mihirsoni
Copy link
Contributor

@erikras but while doing this on our asynVlidation example what I observe is , it is not able to execute on first click , but it does from second click onwards. I am not seeing any console error here though.

@erikras
Copy link
Member

erikras commented May 1, 2016

@mihirsoni I've seen that, too. The two things are related, I think.

@devarsh
Copy link
Author

devarsh commented May 2, 2016

@erikras i've tried to implement it using a promise counter which will keep track of async promises in progress, and as when the promises are resolved they will decrement the counter and during submit call we can check the counter == 0 and proceed with form submission, and we can also eliminate the async call during form submit.

File : reduxForm.js

class Form extends Component {
  constructor(props) {
    super(props)
    this.submit = this.submit.bind(this)
    this.asyncValidate = this.asyncValidate.bind(this)
    this.register = this.register.bind(this)
    this.unregister = this.unregister.bind(this)
    this.fields = {}
    //add a asyncResolved counter
    this.asyncResolved = 0
  }
  asyncValidate(name, value) {
    const { asyncBlurFields, asyncValidate, dispatch, initialized, pristine, startAsyncValidation, stopAsyncValidation, syncErrors, values } = this.props
    const isSubmitting = !name
    if (asyncValidate) {
      const valuesToValidate = isSubmitting ? values : setIn(values, name, value)
      const syncValidationPasses = isSubmitting || !getIn(syncErrors, name)
      const isBlurField = !isSubmitting &&
        (!asyncBlurFields || ~asyncBlurFields.indexOf(name.replace(/\[[0-9]+\]/g, '[]')))

      // if blur validating, only run async validate if sync validation passes and either no
      // blur fields are passed or the field that has blurred is listed
      // if submitting (not blur validation) or form is dirty or form was never initialized
      if (syncValidationPasses && (isSubmitting || !pristine || !initialized) && (isSubmitting || isBlurField)) {
        //increment the counter with each request
        this.asyncResolved = ++this.asyncResolved
        let asyncPromise = asyncValidation(
          () => asyncValidate(valuesToValidate, dispatch, this.props),
          startAsyncValidation,
          stopAsyncValidation,
          name
        )
        //when the promise is resolved or rejected decrement the counter
        asyncPromise.then(()=> --this.asyncResolved,()=> --this.asyncResolved)
        return  asyncPromise
      }
    }
  }
  submit(submitOrEvent) {
    //check if asyncResolved is in progress
    if(this.asyncResolved!=0) {
     console.log('Async call is in progress') // can throw an error or an event not sure.
     submitOrEvent.preventDefault()
     return 
    }
    const { onSubmit } = this.props

    const check = submit => {
      if (!submit || typeof submit !== 'function') {
        throw new Error(`You must either pass handleSubmit() an onSubmit function or pass onSubmit as a prop`)
      }
      return submit
    }
    return !submitOrEvent || silenceEvent(submitOrEvent) ?
      // submitOrEvent is an event: fire submit
      handleSubmit(check(onSubmit), this.props, this.valid, this.asyncValidate, this.fieldList) :
      // submitOrEvent is the submit function: return deferred submit thunk
      silenceEvents(() => handleSubmit(check(submitOrEvent), this.props, this.valid, this.asyncValidate, this.fieldList))
  }
}

@erikras
Copy link
Member

erikras commented May 12, 2016

Published fix as v6.0.0-alpha.9.

@erikras erikras closed this as completed May 12, 2016
@lock
Copy link

lock bot commented Jun 3, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants