foxhound87 / mobx-react-form

Reactive MobX Form State Management
https://foxhound87.github.io/mobx-react-form
MIT License
1.09k stars 129 forks source link

Don't show errors until field is blurred? #248

Closed nuschk closed 7 years ago

nuschk commented 7 years ago

It was hard to come up with a good title for this, sorry.

I'm essentially interested in a feature of redux-form that let's us not show an error on a field unless the field received a blur event or the user submitted the form. This leads to less noise when filling out an empty form, as the user can enter data without being nagged by error displays while typing.

Main use case: Entering e.g. an email-address (or phone numbers or addresses). With mobx-forms, as soon as the users starts to type, the field is touched and has errors and we hence display those errors.

With redux-forms, however, touched is only set after the field receives a blur event or the form is submitted (the field is validated in any case, though). This allows us to only show the error after the user is done with the field.

There's a visited flag that is similar to mobx-form's touched.

Here's a demo that shows the behavior (the redux devtools chrome extension is handy here, as it can display the whole state):

http://redux-form.com/6.6.0/examples/syncValidation/

Just start typing in the email field and compare it to how mobx-forms handles this use case.

Empty form:

bildschirmfoto 2017-03-29 um 16 29 12

Starting to type (no error is shown, although there is one in the state):

bildschirmfoto 2017-03-29 um 16 30 40 bildschirmfoto 2017-03-29 um 16 34 35 bildschirmfoto 2017-03-29 um 16 29 59

(note that there is an error, but no touched)

Pressing moves to the next field and shows the error:

bildschirmfoto 2017-03-29 um 16 30 45 bildschirmfoto 2017-03-29 um 16 30 25

To close, I'm also fine with having to implement this stuff on my side, I'm just very unsure of how to best do that. Any pointers appreciated!

foxhound87 commented 7 years ago

You can actually do that. You can change the validateOnChange Form Option and then implement a new onBlur Event Handler, you can follow this Blog Post to know how to do that using Field Props Bindings

Make sense?

d4rky-pl commented 7 years ago

@foxhound87 might be a good idea to make FAQ more prominent in documentation This has already been answered but discoverability of the answer is pretty low.

Maybe change the name from FAQ to "Common solutions" or something similar and link it in few more places so it's more visible? :)

nuschk commented 7 years ago

Thanks for the pointer. It might be even easier than to disable validateOnChange and fiddle with binding (unless I'm missing something). Can't we something like this:

import { observable, computed, action } from 'mobx';
import { Form, Field } from 'mobx-react-form';

class BlurAwareField extends Field {
  @observable $edited = false;
  @computed get edited() {
    return this.hasNestedFields
      ? this.check('edited', true)
      : this.$edited;
  }

  onBlur = action(() => {
    this.$focus = false;
    this.$edited = true;
  });
}

export default class BlurAwareForm extends Form {
  makeField(data) {
    return new BlurAwareField(data);
  }
}
foxhound87 commented 7 years ago

@d4rky-pl Will be done, thanks.

@nuschk If you want to do that without reimplementing the onBlur handler you can use the observers: check the mobx change.newValue and validate if focus is changed to false. I don't see the need to add a new edited prop. Maybe I'm missing something?

jayalfredprufrock commented 7 years ago

This sounds closely tied to #237. I'd argue that if we had a solution to #237 then not having a built in way to validate on blur wouldn't be a big deal. In both cases, I think what's trying to be avoided is nagging the user with an error message when they haven't even finished typing yet, and if I had to choose a behavior between the two approaches, I'd much prefer some kind of throttled/timeout mechanism as suggested in #237 .

nuschk commented 7 years ago

Ah, yes, #237 is really very related. Sorry, I missed that.

Just to clarify, I don't want to validate on blur (I don't car about validation, actually), I just would like to withhold error display until first blur. After first blur, I want to display errors instantaneously.

I need to store that information ("was blurred once") somewhere. @foxhound87 , you're right, I could just as well use an observer in my field renderers for this, though it will introduce local state in the react component, which feels strange. So, I would welcome having this on the field state.

From the UX perspective, I clearly prefer instantaneous feedback to the user on blur. Also, if there is an error on a field and the user corrects the data, the error message should go away immediately.

IMHO, debounced delayed validation feels awkward to the user. I've tried it, and you'd have to set the delay quite high (not everyone types as fast as we nerds). But then sometimes when you tab or click to the next field, you already start typing before the error in the old one shows up. So, you'd still need to insta-validate on blur, and, even more important, insta-validate when the user corrects his error.

A final thought: As you know, redux-forms is probably one of the most widely used form libraries for react. An they chose to set touched on blur (well, actually, it's configurable: http://redux-form.com/6.6.1/docs/api/ReduxForm.md/, but that's the default) precisely to control error rendering. I think that's certainly a battle tested solution. Plus, as more and more people convert their redux apps to mobx, you'll likely get many converts such as myself, so it would be a plus to have similar concepts. 😉

I'll close this issue and move over to #237 .

foxhound87 commented 7 years ago

@nuschk the touched prop in mobx-react-form is already true on onFocus, then your previous suggestion with the edited prop is the only way, this is different from the redux-forms (which I don't like because don't feel right to have touchOnBlur, it should be touched when you actually interact with the input).

nuschk commented 7 years ago

@foxhound87 I agree with naming that prop touched is misleading. I find your definition much less confusing. edited is not really better, though, so I'm still looking for a better name... 😄

foxhound87 commented 7 years ago

@nuschk something like interacted? or can be confused with touched?

nuschk commented 7 years ago

interacted is certainly better than edited. Yeah, it's not crystal clear either, but, well, naming is hard.

You could also name it by its purpose and thus showError and put it next to hasError. This way, of course, it could only ever be true if the field has errors.