react: Bug: Backspace in input type="number" behaves badly in Blink

This appears to have been introduced in a new Chrome version, but I can’t find any reference to where.

Affected/Tested Browsers (OS X):

  • Chrome 51.0.2704.106 (64-bit)
  • Opera 39.0.2256.15

Unaffected Browsers:

  • Safari 9.1
  • Firefox 49

Backspacing in an input element with value or defaultValue set causes some very odd behavior. Once a decimal point is gone, it can’t easily be re-added.

Example:

react-input-bug

In this example, I simply backspaced twice. On the second backspace, when I expect 3. to be showing, the input instead reads 3 and the cursor has moved to the beginning. The next two jumps are my attempts to add another decimal point.

Fiddle: https://jsfiddle.net/kmqz6kw8/

Tested with React 15.2.

Notes: This only occurs when value or defaultValue is set. If neither is set, the input behaves properly. We are currently working around this issue by (unfortunately) setting the input value on componentDidMount via a ref.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 4
  • Comments: 29 (25 by maintainers)

Most upvoted comments

Fixed! https://jsfiddle.net/09Lxxzn9/1/

Thanks for your hard work @nhunzaker.

@nksfrank I believe the plan is to ship this with 15.5: https://github.com/facebook/react/issues/8854

@nhunzaker I don’t know any quick reference points other than https://github.com/facebook/react/issues/6556, but the basic problem was that we couldn’t get the actual input value (as entered by the user) when the input was in an invalid state (eg. as the user is typing “3.14”, there is an illegal intermediate state “3.”) and setting the last known legal value would remove the decimal point. Firefox’s fix was to not fire onInput for illegal states, Chrome’s fix was to not remove the decimal if the value is approximately the same (“3” instead of “3.”).

You’ll need to do a little digging to figure out the exact behaviors of the various browsers and figure out what a good fix would look like. I don’t know the answer off the top of my head, but it looks like things are now sufficiently fixed on the browser’s end, so this should be fixable now in React.

FWIW, it may be worth tracking keystrokes and cursor events and paste events, and maintain a perfect shadow state. Since we don’t have to deal with non-numeric-ish characters, we don’t need to worry about foreign languages (which is where all the input edge cases come from). I think this would allow us to simulate what the browser would do (allow us to know what value is actually in the text box) and sidestep all the weirdness of the type=number spec. Anyway, I’ll let you guys figure that out.

Number inputs are broken in React, due to https://github.com/facebook/react/issues/6556, which was filed before #6406 was merged. You’re going to see the same problem with email inputs (as per https://github.com/facebook/react/issues/6368, which is basically the same issue, again filed before #6406 was merged).

It is entirely possible that #6406 exposes that bug for defaultValue, but the bug was always there in the React core and was always visible for controlled inputs.

This has been broken in React for as long as I can remember:

var NumberInput = React.createClass({
  getInitialState: function() { return {value: 3.14}; },
  render: function() {
    return <input type="number" value={this.state.value} onChange={(e)=>{this.setState({value: e.target.value});}} />;
  }
});

ReactDOM.render(
  <NumberInput />,
  document.getElementById('container')
);

A workaround is to set type=text and do manual value filtering to ensure that only numbers are entered into the textbox. This has the downside that you don’t get the numeric keyboard on mobile, but we’ll need to figure out a solution to https://github.com/facebook/react/issues/6556 to handle that properly.