React: Number input breaks when letter "e" is entered

Created on 28 Aug 2018  路  14Comments  路  Source: facebook/react

Do you want to request a feature or report a bug?
bug

What is the current behavior?
input[type=number] only allows entering numbers and letter "e" in the input. Native "input" input event is called for both numbers and the letter "e". With React the onChange event is only called for numbers. There's no way to catch "e" with onChange.

The "e" is even being filled when the input is controlled. The only way I can think of to work around this bug right now is to use onKeyDown and preventDefault when "e" or "E" is pressed.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
https://codesandbox.io/s/ov3ql3ljwz

What is the expected behavior?
It should pass anything that is being filled into the input to the onChange handler and should not break controlled component.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React: 16.4.2
Chrome: 68.0.3440.106
Windows 10

DOM Needs Investigation

Most helpful comment

This is still an issue.

All 14 comments

That's the browser behavior, cause 1e1 is also valid-floating-point-number.
But if you set input.value = '1e', React will throw warning and just give empty to input.

From what I could experiment with your codesandbox, the onChange is still being called (with an empty value) for when you type "e".
Additionally, if you type anything after the "e", it will send the value properly.

I believe this is happening because a string _ending_ with "e" is not a valid number (e.g. 12e), but a string with "e" followed by an integer is (e.g. 12e2). Remember that 12e2 is a valid number, as Ende93 pointed out.

Notice the onChange will be called with an empty value for any other non-number string (e.g. foo), so it looks quite consistent to me.

@Yurickh is spot on. Thanks!

If I type the characters 1e2 I see three change events:

  1. "1"
  2. "" (because 1e is not a valid number)
  3. `"1e2"

I believe everything is behaving as intended. However please let me know if there's a case we've missed. Also this is a super confusing part of inputs, so I'm happy to continue the conversation if anything else needs clarification (or I've missed something).

Thanks guys for having a look at this, but this is all I am getting: https://drive.google.com/file/d/1Tq7fF3e2w3QyAwE4mLTpHKz__GYYK0wF/view

The onChange never gets called and the input becomes uncontrolled.

I believe this is a bug as the native input event gets called correctly.

onChange get called, you can just see the console log, but when you input 1e, e.target.value is '', and setState get ''(empty string), so e.target.value is the same to setState, so React will not see the diff, and input will not change.

That's correct. Numbers that start with e are invalid, so Chrome reports an empty string as input.value. As more numbers are added, the value reports the same (an empty string), so no change occurs.

@nhunzaker the problem here is that React's onChange doesn't allow to distinguish between empty inputs of type number and non-empty invalid ones. This is often needed, e.g. to implement Material Design-style floating labels that descend into the input when it's empty; see e.g. https://github.com/mui-org/material-ui/issues/12764. I assume this is because in both cases value is an empty string.

Modifying your steps, the scenario is as follows:

  1. Enter "2" into the input - the callback fires.
  2. Remove "2", making the input empty - the callback fires.
  3. Enter "e" - the callback doesn't fire.

To implement Material UI inputs you need to check input's validity.badInput field. But you won't have a chance to actually see it.

Could onChange be fired not only if the input value changes but also if its validity.badInput field changes? It seems that if anything else is not valid with the input (e.g. the value is below min or step is not respected) the value field is not zeroed out.

Note that IE doesn't support validity.badInput but IE also doesn't always clear value for non-number values in a number input. For example, according to:
https://github.com/angular/angular.js/issues/10981#issuecomment-73231004
0a is not cleared in IE but a0 is. This problem might not be solvable, though.

@mgol Would #9657 resolve this?

@gaearon If I understand it correctly, this line:

onInput (...) It is allowed to over-fire many events even if nothing changed.

suggests it should solve the issue as the browser fires the input event for each change, even if it doesn't change the value property because, for example, you type e into an empty <input type="number">.

I don't know what's the difference between the polyfilled and non-polyfilled onInput, is that relevant here? This particular issue affects both controlled and uncontrolled inputs.

@mgol Is this issue about controlled or uncontrolled component? Or both?

Both.

Tentatively reopening for future consideration.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

This is still an issue.

Was this page helpful?
0 / 5 - 0 ratings