React: Calling onBlur and mutating state can cause onClick to not fire.

Created on 24 Jun 2015  路  12Comments  路  Source: facebook/react

We encountered this while trying to have different parts of form validation happen on both the onBlur and the onClick. It seems that if you update State on an onBlur, and that state element is injected into the DOM via JSX, positionally ABOVE a button with an onClick--the onClick will not fire.

I believe this is related to this issue: https://github.com/facebook/react/issues/2291

See this http://jsfiddle.net/kb3gN/11477/ which is a variation on the example provided above. If you enter some text, you'll have to click the button twice, to see click: true.

Not 100% confident that order matters in the DOM, but you can see it consistently reproducable in the JSFiddle above.

@andela-domonori first noticed the weird behavior.

Most helpful comment

I don't think it has to do with the button moving. I did a similar test without the button moving and submit did not fire. It looks like if blur causes a re render, submit is not even fired.

All 12 comments

This is browser behavior and it seems entirely correct to me. The button receives the mouse down event but since the element moves immediately after then mouse up happens outside and no click is registered. Do the same thing but move your cursor back over the button first and then release, now you get the click as expected.

Confirmed, it looks like the issue is that you're not getting a click as a result of the button moving. If you put the error output under the button (such that it won't affect the positioning of the button), everything behaves as expected (so it's not an issue related to mutating state, it's an issue related to you moving things in the dom). Also, all the things @syranide pointed out. I'm going to close this as "not a react issue".

I don't think it has to do with the button moving. I did a similar test without the button moving and submit did not fire. It looks like if blur causes a re render, submit is not even fired.

@OndeVai OndeVai - This is a valid issue. I am also encountering this

For me there is no error in any state. My form is filled and valid. But I am doing field validation on blur event then my component is rerendering and onClick event is not firing.

I switch my "isEditState" flag on my "EditableField" component in onBlur for the input which fired when I click "Ok" button to save input text (And on clicking somewhere else I drop text changes in that onBlur callback). So the onClick event doesnt fire when 'Ok' clicked. I do not do any movements in a DOM tree.

Workaround:

  const blurTextField = (e) => {
    if (isEditState && isNodeClassContain(e.relatedTarget,'editButton')) {
      setIsEditState(false);
    }
  };

  useEffect(() => {
    document.addEventListener('click', blurTextField);
    // some stuff may be here
    return () => {
      document.removeEventListener('click', blurTextField);
    };
  }, [isEditMode]);

The point is not to use onBlur. And use click or focus event listener on document with detecting which element focused or clicked exactly (by class or id or whatever you want).

It seem like blur event follow by setState causes a rerender component. I have a simple solution to improve:
using onMouseLeave event instead of using onBlur
But it's not perfect solution because my input will lost typing when mouse leave the input

It seem like blur event causes a rerender component. I have a simple solution to improve:
using onMouseLeave event instead of using onBlur
But it's not perfect solution because my input will lost typing when mouse leave the input
onMouseLeave={hideMe}
...
...
hideMe = (e) => {
const currentTarget = e.currentTarget;
setTimeout(()=>{
if (!currentTarget.contains(document.activeElement)) {
this.setState({ showSuggestions: false })
}
}, 0);
};
this works!

i experienced the same issue and found a workaround.

onBlur={() => setTimeout(onBlur, 500)}

override the onBlur and delay it a bit. basically delay what ever should happen after onBlur by about 500ms.

@anton-thushara @dashName @jimfb Could you look into problem please? A codesandbox is also given. https://stackoverflow.com/questions/63686646/stop-handleblur-from-doing-anything

The work around using setTimeout works because events are fired in order of priority. The event listener click comes after blur in priority. This causes a blur to fire but not the click. You can use mousedown instead of click due to it's priority over blur. By using setTimeout on the blur it delays the blur and allows for the click to take priority.

If there is a React bug here please file a new issue. We don鈥檛 track discussions on closed issues.

Was this page helpful?
0 / 5 - 0 ratings