Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.js controlled text cursor focus issue

I have a simple controlled input of type number like below.

<input type="number" value={+value} step={1} onChange={this.updateMyChange} />

My value often returns a decimal number like 123.123 . My problem is, when I try edit the value. The cursor loses focus and shifts to the beginning ignoring the whole numbers as soon as the decimal places are cleared. Like below:

enter image description here

How do I address this? Immediately after the decimal places are cleared, the cursor jumps to the beginning thereby making it impossible to edit the whole numbers. Any help would be appreciated.

Update Below is the remaining code as requested by the user below.

render() {
   const {value} = this.state;

   return (
      <input type="number" value={+value} step={1} onChange={this.updateMyChange} />
   )
}

And my updateMyChange method is simply

updateMyChange(e) {
  this.setState({ value: e.target.value });
}

It does nothing much simply sets the new value. The cursor position jumps to the end as soon as decimal places are cleared. It does not set cursor for whole numbers.

like image 212
Nuru Salihu Avatar asked Jul 22 '16 03:07

Nuru Salihu


1 Answers

This is how React updates an input field's value:

node.setAttribute(attributeName, '' + value);

When you set value attribute using that method, the caret goes to the beginning of the field, regardless of using React. You can see what I am saying in this fiddle - https://jsfiddle.net/5v896g3q/ (Just try and position the cursor in the field, between changes).

According to MDN, setAttribute is unstable when dealing with value. The recommended way of changing value is by accessing the value property of the element, like element.value = newValue. If you use that approach, all seems to go as expected.

This is all I can tell for sure. Now let's speculate a little. When you type anything in that field, you are:

  1. updating the value of the input
  2. reading that value, and sending to React as state
  3. React updates the value of the input, with the new state

When you are typing on the field, step 3 is likely to have no impact, because when the value comes back, the input already got it right. Except on the case with the float number. When your field reads 1., the actual value React updates the field with is 1. And React uses the evil method (setAttribute).

So, a workaround I found was setting the value of the field, using the proper method, before React touches it, on componentWillUpdate:

componentWillUpdate(nProps, nState){
  this.refs.input.value = '0' + nState.value
}

The problem there, is that it is "numerizing" the value on every change, meaning I won't be able to have a point (1.). For that reason, I will only edit the input in case new value is 2 characters shorter than the old one (point + digit after point):

componentWillUpdate(nProps, nState){
  if(this.state.value.length - nState.value.length === 2){
    this.refs.input.value = '0' + nState.value
  }
}

Working example - https://jsfiddle.net/bsoku268/3/

note: the fiddle is for demonstration purposes, and not supposed to be a bulletproof solution, as there are many ways of interacting with an input field, such as copy & paste, drag & drop, autofill, etc

like image 196
Hugo Silva Avatar answered Sep 19 '22 13:09

Hugo Silva