0

I have a controlled number input in a React app, and when I remove all the digits right of the decimal point, the decimal point itself is removed. So when I have 1.1, and remove the rightmost 1, it should leave 1.; but the point is also removed automatically, leaving 1, and the cursor is moved to the front.

For a minimal example, see this fiddle: https://jsfiddle.net/sashee/e00s7h9d/ . Just press backspace on the input to observe the behavior.

After some googling, I think the root of the problem is that 1. is treated as 1, and there is no way to extract the current value from the input.

Therefore, when I remove the rightmost 1, the following happens:

  • The change handler is called
  • The value is 1
  • React updates the value in the state to 1
  • The input now has the value of 1, instead of 1.

As I see, this is not a bug, but a weird outcome of the architecture. Are there any workarounds that use number inputs? Or should I fall back to regular text inputs?

Update:

It works fine in Firefox, but not in Chrome.

Tamás Sallai
  • 3,195
  • 1
  • 14
  • 25

3 Answers3

1

It turned out to be a bug in React itself, see this PR.

It is fixed from 15.5.4 onwards, see this fiddle:

<script src="https://unpkg.com/react@15.5.4/dist/react-with-addons.js"></script>
<script src="https://unpkg.com/react-dom@15.5.4/dist/react-dom.js"></script>
Tamás Sallai
  • 3,195
  • 1
  • 14
  • 25
0

Is there a reason why you would like to have that decimal in place when you delete the number proceeding the decimal point? This isn't so much as a React question as it is more a general HTML one. Like you mention, if you would like the decimal point to show when you are inputing a number, then you can change the type="number" to type="text".

Also, I'd like to point out that in your initial state, you are setting the value to a string, and then the string "1.1" is being coerced into the number 1.1. For consistency you should set the initial state to a number if your input is type="number" or keep the initial state as a string and change the input to type="text"

If you would like the number in the box to increment or decrement by 0.1 every time the up or down arrow are pressed, change the step to step="0.1".

Yo Wakita
  • 5,322
  • 3
  • 24
  • 36
  • Actually, the e.target.value returns strings, and not numbers, so it's consistent. One possible use case for this is to change 1.1 to 1.25 .In this case I click at the end, delete the last 1, and enter 25. But as the number I'm editing changes, I can't do it. – Tamás Sallai Feb 08 '17 at 19:06
  • Oh I see the issue now, I apologize I misunderstood the problem. I believe this question provides good insight to the OP's problem: http://stackoverflow.com/questions/39672804/failed-to-parse-4-to-4?noredirect=1&lq=1 – Yo Wakita Feb 08 '17 at 19:15
0

The issue is that you're defining your input as type="number" but the value you're throwing into it a string. When the string "1." gets converted to a number, it loses the decimal value.

// Example of string "1." getting converted to a number
parseInt("1.", 10) // 1

How to fix:

As @Yo Wakita says in his answer, just use a string input. However! You can easily disallow non-numbers with a RegEx:

_onChange(e) {
  // This will match all signed/unsigned floats
  // Pattern credit: http://stackoverflow.com/a/16951568/2518231
  // You'll probably want to store this as a default prop so that it isn't generated on every onChange
  var regex = new RegExp(/^-?\d*\.?\d*$/);

  if (regex.test(e.target.value) ) {
    this.setState({val: e.target.value})
  } else {
    alert('Sorry, only numbers are allowed.');
  }
},
Matthew Herbst
  • 29,477
  • 23
  • 85
  • 128