1

I have a following class

export default class TestInput extends Component {

    state = {
         modified: false
    };

    change = e => {
         this.setState({ modified: true });
         this.props.input.onChange(e.target.value);
    };

     render() {
        return (
            <input type="text" value={this.props.input.value} onChange={this.change} className={!this.state.modified && this.props.meta.pristine ? 'default' : 'modified'} />
        );
    }
}

that I'm using like this

<Field component={TestInput} name="testProp" />

Whenever I place a cursor in the middle of the text in the field and write a letter, the letter appears at the right place but the cursor jumps to the end of the field. This is caused by the line this.setState({ modified: true }); and subsequent class change. If I comment out this line, this is not happening. I don't understand it. What am I doing wrong? Can I do something about it?

samuelg0rd0n
  • 1,080
  • 2
  • 15
  • 27
  • You can check the answer here https://stackoverflow.com/questions/35535688/stop-cursor-jumping-when-formatting-number-in-react/59188168#answer-59188168 – Sagar Acharya Dec 05 '19 at 08:11

4 Answers4

2

I am encountering this issue right now.

Here is how I am solving it. Most modern browsers including ie11 support a property on input dom nodes called selectionStart, this property indicates where cursor position is, (note it is not 0 indexed, but begins at 1). The sibling setter property is setSelectionRange(begin, end). If you can capture the actual node's cursor position when its change event is fired, you can set the node's cursor position after the component has rendered, and the input thinks it has received new text input moving its cursor to the end of the new input.

Disclaimer: YOU WILL HAVE TO USE REACT REFS

Depending on your stack, the code might look like this.

class MyForm extends React.Component {
   constructor(props) {
      super(props)
      this.state = { selectionStart }
      this.inputElRef = React.createRef();
      this.preserveCursor = this.preserveCursor.bind(this)
      this.setCursor = this.setCursor.bind(this)
      this.handleOnChange = this.handleOnChange.bind(this)
   }
   preserveCursor() {
      this.setState({ selectionStart: this.inputElRef.current.selectionStart })
   }
   setCursor() {
      this.inputElRef.current.setSelectionRange(
         this.state.selectionStart,
         this.state.selectionStart,
      )
   }
   handleOnChange() {
      this.preserveCursor()
   }
   componentDidUpdate() {
      this.setCursor()
   }
   render() {
     const { ...props } = this.props;
     <div>
       <form>
          <input ref={this.inputElRef} { ...props } onChange={this.handleOnChange} name="foo" />
      </form>

   }
}
export default connect()(reduxForm({ name: 'MyForm' })(MyForm)

Note: If you use something like react-toolbox instead of a raw dom input you will need to pass innerRef instead of ref, and that value should be a bound function of the form (el) => { this.myProperty = el }, which will give you access to the ref even though react-toolbox does not have awareness of ref, the extend react component has awareness of innerRef...

sloan-dog
  • 85
  • 5
1

If you are using a value from the redux store in the textfield and updating the value of the redux store onChange. The problem is observed from react-redux version 6.0.1 and higher. Change your react-redux version to 6.0.0 and the cursor jumping problem should be solved.

Rishith Poloju
  • 171
  • 1
  • 3
0

This happens on the onChange event, i.e, every time you try to change the value of the input your change() function gets fired, in which you have a this.setState({ modified: true }) as you might know that this.setState forces the component to rerender, and know this time since !this.state.modified will return false, so the class gets changed to modified, which means it is working just fine, the cursor jumping to the end might have something to do your modified class, other than that nothing's wrong.

You might need to look and rephrase your question, what is it that you want should happen?

aditya
  • 339
  • 2
  • 12
  • 1
    I just need the cursor NOT to jump to the end of the input. That's all. It has definitely nothing to do with the class itself. It's just an example. The classes actually don't exist. If I don't change the class, the cursor stays at its correct place after the letter I wrote. So what should I do differently? – samuelg0rd0n Jun 01 '17 at 12:22
  • what do you want to do in the first place, If you don't want the cursor to go anywhere, why don't you just, remove conditional statement of the changing class, and it definitely has got something to do with your class, if you say **If I don't change the class, the cursor stays at its correct place** – aditya Jun 01 '17 at 12:33
  • But that's not a solution :-( I need to change the class of the input. That's a legit requirement, isn't it? But at the same time this somehow screws up with the cursor and I'm simply lost what I'm doing wrong or what other approach I should use. – samuelg0rd0n Jun 01 '17 at 12:38
  • And if I add to the render() method for example setTimeout(() => { this.setState({ modified: !this.state.modified }); }, 2000); and place the cursor somewhere to the middle of the input, it changes the class every 2 seconds but the cursor stays at the same position. So it has nothing to do just with changing the class. – samuelg0rd0n Jun 01 '17 at 12:40
  • If you say it has nothing to do with the class, just change the class name to something else instead of 'modified' – aditya Jun 01 '17 at 12:42
  • OK. I wrote it in a bad way. It has really nothing do to with the class. I can omit the className part altogether. All it matters is that I change the state and I don't need to use the state anywhere. Just changing the modified variable in state causes the cursor to jump. – samuelg0rd0n Jun 01 '17 at 13:04
  • Yeah sure, you're `this.props.input.onChange(e.target.value)` might have been the culprit – aditya Jun 01 '17 at 13:07
0

Just change value to defaultValue. It will work perfectly.

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/33258302) – André Nov 30 '22 at 15:17