React-bootstrap-typeahead: blur event triggers before change event

Created on 31 Aug 2017  路  6Comments  路  Source: ericgio/react-bootstrap-typeahead

Version

1.4.2

Behaviour

Blur event triggers before change event.
I'm integrating react-bootstrap-typeahead with redux-form. Redux-form has asyncValidate function which triggers given function on input blur event passing values from input to it.

asyncValidate: (values) => void; to be precise.

When you choose one of the options from typeahead, asyncValidate method gets empty values.
e.g. if I have typeahead input with emails, and I choose [email protected] email from options, asyncValidate receives { email: '' } and if I'm validating that email on server, server receives empty string because blur event fired before change event and redux-form didn't have the time to update that field.

Ignore the console.log TSLint :)
image

not a bug

Most helpful comment

Redux forms triggers that method, asyncValidate, for given fields, on blur event so unfortunately I can't modify that behaviour. What it does is that (if you have experience with redux) redux forms dispatches change action when input is change and after that when you lose focus it dispatches blur action which then calls asyncValidate method which you defined, in my case I call server with values, validate them and return response.

e.g.
reduxForm({form:'formName', asyncValidate: validateSomething, asyncBlurFields: ['field1', ... ]}

I made myself a workaround though. I pass boolean property to typeahead component and if that property is true than I trigger redux forms onBlur method within typeaheads onChange and get wanted behaviour.

All 6 comments

This is expected based on the current implementation. The onChange event is triggered by the menu item onClick handler; click events are fired after blur events, therefore onBlur is called first.

Why are you validating in the blur rather than the change event?

I have experienced this as well.

In my case, the input text stays in the field when the user leaves it and focuses on another item

Would like to be able to do some validation to make sure an option was selected and clear the input text if a valid option was not selected when the component loses focus

If you're using the component in a controlled way, then you should be able to do your validation in your onChange callback. If it doesn't validate, clear the input. If it does, set the input. I don't understand the issue here. A fiddle or other working example would be helpful.

Redux forms triggers that method, asyncValidate, for given fields, on blur event so unfortunately I can't modify that behaviour. What it does is that (if you have experience with redux) redux forms dispatches change action when input is change and after that when you lose focus it dispatches blur action which then calls asyncValidate method which you defined, in my case I call server with values, validate them and return response.

e.g.
reduxForm({form:'formName', asyncValidate: validateSomething, asyncBlurFields: ['field1', ... ]}

I made myself a workaround though. I pass boolean property to typeahead component and if that property is true than I trigger redux forms onBlur method within typeaheads onChange and get wanted behaviour.

I have a case similar to @tomislavhren where i'm doing async validation too.
The expected functionality is that whenever the input loses focus (i.e. blur), the current value is validated.

As it's been made clear, the input blurs before the menu hides and the selection is made. This could be to do with react event orders, or that the menu hides on mouseUp but the blur happens on mouseDown.

In my case, i found the cleanest solution to "fix" the blur behaviour was to save the blur event, and defer the onBlur callback if the menu was showing. Then execute onBlur with the saved event after the menu hides.

onMenuShow={() => {
    this.menuShowing = true;
    onMenuShow && onMenuShow();
}}
onMenuHide={() => {
    this.menuShowing = false;
    onMenuHide && onMenuHide();
    if (this.defferedBlur) {
        onBlur && onBlur(this.defferedBlur);
    }
}}
onBlur={(e) => {
    if (!this.menuShowing) {
        return onBlur && onBlur(e);
    }
    e.persist();
    this.defferedBlur = e;

Using the default behaviour and relying on the onMenuHide callback worked for most cases, except the case where the user types a value which doesn't match any option and no menu shows before blur. This example gets around that without breaking anything apparent

v2.4.0 addresses #310 (and probably this issue) by preventing the input from blurring when a menu item is clicked. I'm going to close this issue out regardless since I don't consider it a bug.

Was this page helpful?
0 / 5 - 0 ratings