Manually set redux-form field and/or form errors
Asked Answered
W

5

10

I'm aware that if you throw a SubmissionError from your handleSubmit() function, the redux-form code will fill in the errors of the appropriate fields and/or the form itself.

Yet that API of setting field/form errors, tightly couples our implementation of handleSumbit() to be a caller of the redux-form code (which contains the SubmissionError exception handler).

My use case is to have something like so:

function asyncActionDispatcher(values) {                                     
  return (dispatch, getState) => {                                           
    // I'm using getState, which is not accessible in handleSubmit()         
    // But I'd also like to be able to set errors on the form fields and/or the
    // form.                                                                 
  };                                                                         
}                                                                            

function handleSubmit(values, dispatch) {                                    
  dispatch(                                                                  
    asyncActionDispatcher(values)                                            
  );                                                                         
} 

I can't throw a SubmissionError in asyncActionDispatcher() because it's called by redux and not redux-form.

Does redux-form have another API to set errors on fields/form?

Wormwood answered 7/2, 2017 at 8:37 Comment(0)
J
8

Yes, it does.

You can use validate and asyncValidate props to set validator functions for the values of your form (synchronous and asynchronous respectively).

validate should be a function that either returns an empty object if validations pass or an object detailing validation errors in the form { someField: 'some error', otherField: 'other error' }

asyncValidate on the other hand should return a Promise that is resolved normally if validations pass or that is rejected with an object detailing validation errors (see above) if validations do not pass.

handleSubmit is implemented in such a way that it runs both synchronous and asynchronous validations before calling onSubmit, so if you've set up validate and asyncValidate, those errors should appear in your form/fields automatically without any additional work.

Hope this helps!

Update:

You could also just wait until asyncActionDispatcher finishes and continue submitting or reject with a SubmissionError depending on what the results from it were.

function asyncActionDispatcher(values) {
  return (dispatch, getState) => {
    if (success) {
      return Promise.resolve('success')
    } else {
      return Promise.reject('error')
    }
  }
}

function onSubmit(values, dispatch) {
  // dispatch whatever action you need to dispatch
  return dispatch(asyncActionDispatcher(values))
    .then(() => {
      // i guess you need to do your actual submitting here
    })
    .catch(error => {
      return new SubmissionError({ _error: 'whatever you want here' })
    })
}

// and in the component
<form onSubmit={ handleSubmit(onSubmit) }> ... </form>

Sources: Redux-Form docs, wrapped component props documentation, synchronous validation example, asynchronous validation example

Jaymie answered 7/2, 2017 at 19:19 Comment(4)
validate and asyncValidate still seem to require the same caller/callee coupling as I mentioned and neither provide you with getState(), in order to always get the latest state values.Wormwood
I updated the answer to maybe answer to your particular need. If that isn't it, you'll need to edit your question to be clearer about what it is that you're actually trying to achieve.Jaymie
Your updated answer does indeed solve the problem of granting access to getState() and being able to set form/field errors. Both our answers are now slightly different means to the same end, but I'll give the answer to you, because yours makes use of the dispatch() return value nicely.Wormwood
Actually before I grant you the answer, I'd like to edit yours, to cut out the original answer pertaining to validate and asyncValidate, because I was never interested in client-side validation. I just wanted to get access to getState() while still being able to set errors on the form/fields.Wormwood
S
11

Does redux-form have another API to set errors on fields/form?

Another option here when asyncValidate and other options proposed won't work (for example because validations consider multiple forms) is to dispatch updateSyncErrors directly. Example usage below:

const { updateSyncErrors } = require('redux-form/lib/actions').default;

dispatch(updateSyncErrors('formName', {
  fieldName: 'Some Error Message'
}));
Sonni answered 6/3, 2019 at 0:26 Comment(2)
ur a life saverJuice
Calls it twice for me. First time with the error message, then second time it clears it.Nedi
J
8

Yes, it does.

You can use validate and asyncValidate props to set validator functions for the values of your form (synchronous and asynchronous respectively).

validate should be a function that either returns an empty object if validations pass or an object detailing validation errors in the form { someField: 'some error', otherField: 'other error' }

asyncValidate on the other hand should return a Promise that is resolved normally if validations pass or that is rejected with an object detailing validation errors (see above) if validations do not pass.

handleSubmit is implemented in such a way that it runs both synchronous and asynchronous validations before calling onSubmit, so if you've set up validate and asyncValidate, those errors should appear in your form/fields automatically without any additional work.

Hope this helps!

Update:

You could also just wait until asyncActionDispatcher finishes and continue submitting or reject with a SubmissionError depending on what the results from it were.

function asyncActionDispatcher(values) {
  return (dispatch, getState) => {
    if (success) {
      return Promise.resolve('success')
    } else {
      return Promise.reject('error')
    }
  }
}

function onSubmit(values, dispatch) {
  // dispatch whatever action you need to dispatch
  return dispatch(asyncActionDispatcher(values))
    .then(() => {
      // i guess you need to do your actual submitting here
    })
    .catch(error => {
      return new SubmissionError({ _error: 'whatever you want here' })
    })
}

// and in the component
<form onSubmit={ handleSubmit(onSubmit) }> ... </form>

Sources: Redux-Form docs, wrapped component props documentation, synchronous validation example, asynchronous validation example

Jaymie answered 7/2, 2017 at 19:19 Comment(4)
validate and asyncValidate still seem to require the same caller/callee coupling as I mentioned and neither provide you with getState(), in order to always get the latest state values.Wormwood
I updated the answer to maybe answer to your particular need. If that isn't it, you'll need to edit your question to be clearer about what it is that you're actually trying to achieve.Jaymie
Your updated answer does indeed solve the problem of granting access to getState() and being able to set form/field errors. Both our answers are now slightly different means to the same end, but I'll give the answer to you, because yours makes use of the dispatch() return value nicely.Wormwood
Actually before I grant you the answer, I'd like to edit yours, to cut out the original answer pertaining to validate and asyncValidate, because I was never interested in client-side validation. I just wanted to get access to getState() while still being able to set errors on the form/fields.Wormwood
W
1

Your handleSubmit() is called like so:

handleSubmit(values, dispatch, props)

...where one of the properties of that props Object is a stopSubmit() function.

That's what you want to use, where it's API is similar to as described here:

stopSubmit(form:String, errors:Object)
// Flips the submitting flag false and populates submitError for each field.

You would need to specify the unique form string identifier when importing that function yourself and using it, but if you use the version past to your handleSubmit() (via the props Object), then you don't need to provide the argument for the first parameter.

Wormwood answered 7/2, 2017 at 22:50 Comment(0)
E
0

I'm throwing SubmissionError from the .catch() callback of axios, which is called from inside a thunk, so I think you should be able to do the same from within ReduxSaga. Just make sure to return so that you don't get an uncaught exception error. In my case, the thunk returns the axios call, and inside of .handleSubmit() I return the thunk.

Epley answered 6/5, 2018 at 19:9 Comment(0)
H
0

the onSubmit callback has values, dispatch, props arguments. you can also use the updateSyncErrors method

props.updateSyncErrors({form_field_name: "Some field level error"})
Hairstyle answered 1/2, 2022 at 8:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.