Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop cursor jumping when formatting number in React

Tags:

reactjs

I have an input field on my react component that shows the line price for an item (two decimal places with thousands separators). I want the value shown to be in money format when the component first renders and also to be kept in money format as user types in the field.

At the moment I have the following code in my component:

var React = require('react');
import accounting from 'accounting';

MoneyInput = React.createClass({
    propTypes: {
        name: React.PropTypes.string.isRequired,
        onChange: React.PropTypes.func.isRequired,
        value: React.PropTypes.number,
        error: React.PropTypes.string,
    },

    onChange(event) {
        // get rid of any money formatting
        event.target.value = accounting.unformat(event.target.value);

        // pass the value on
        this.props.onChange(event);
    },

    getValue() {
        return accounting.formatNumber(this.props.value, 2)
    },

    render() {

        return (
                <div className="field">
                    <input type="text"
                           name={this.props.name}
                           className="form-control"
                           value={this.getValue()}
                           onChange={this.onChange} />
                    <div className="input">{this.props.error}</div>
                </div>
        );
    }
});

module.exports = MoneyInput;

That code displays the data correctly formatted, but every time I enter a value the cursor jumps to the end of the number.

I understand why that's happening (I think) and I've read several questions here related to not losing cursor position in JavaScript (here and here for example).

My question is what's the best way to deal with this in React?

I think that ideally I wouldn't want to store the cursor position in state (e.g. I would want these to be Presentation Components in Dan Abramov syntax) so is there another way?

like image 648
tomRedox Avatar asked Feb 21 '16 11:02

tomRedox


3 Answers

An easy solution for losing cursor/caret position in the React's <input /> field that's being formatted is to manage the position yourself:

    onChange(event) {

      const caret = event.target.selectionStart
      const element = event.target
      window.requestAnimationFrame(() => {
        element.selectionStart = caret
        element.selectionEnd = caret
      })

      // your code
    }

The reason your cursor position resets is because React does not know what kinds of changes you are performing (what if you are changing the text completely to something shorter or longer?) and you are now responsible for controlling the caret position.

Example: On one of my input textfields I auto-replace the three dots (...) with an ellipsis. The former is three-characters-long string, while the latter is just one. Although React would know what the end result would look like, it would not know where to put the cursor anymore as there no one definite logical answer.

like image 96
dmitrizzle Avatar answered Sep 29 '22 15:09

dmitrizzle


Hi there has been a discussion about this on the issues in github.com https://github.com/facebook/react/issues/955 see the comment on Nov 30th

You probably want to be using setstate on input components aswell.

like image 37
mike james Avatar answered Sep 28 '22 06:09

mike james


onKeyUp(ev) {
  const cardNumber = "8318 3712 31"
  const selectionStart = ev.target.selectionStart; // save the cursor position before cursor jump
  this.setState({ cardNumber, }, () => {
    ev.target.setSelectionRange(selectionStart, selectionStart); // set the cursor position on setState callback handler
  });
}
like image 35
Khyati Avatar answered Sep 28 '22 15:09

Khyati