React: Cursor jumps to end of input when onChange doesn't call setState

Created on 8 May 2018  路  7Comments  路  Source: facebook/react

Current Behaviour
An input onChange function that returns a value equalling the prior value causes the cursor to jump to the end of the input. This is the same as this comment from #995 formally raised as a feature request.

Repro sandbox: https://codesandbox.io/s/n4k3yx47j
That same code:

import React from "react";
import { render } from "react-dom";

class Input extends React.Component {
  state = { value: "TypeANumber" };

  onChange = e => {
    let nextValue = e.target.value;

    if (/[0-9]/.test(nextValue)) {
      nextValue = this.state.value;
    }
    this.setState({ value: nextValue });
  };

  render() {
    return (
      <input
        type="text"
        value={this.state.value}
        onChange={this.onChange}
      />
    );
  }
}

render(<Input />, document.getElementById("root"));

What is the expected behavior?
I'd like the cursor not to jump in the special case where the returned changed value is a rejected change i.e. the 'noop' change.

I understand fully that react cannot predict cursor position if the value is _changed_ in onChange, ~however I cannot currently find an npm module that allows free-length regex filters (vs a fixed length mask)~ or a way to implement a filter myself, without the cursor jumping in this case.

Regarding the non-clarity of how to deal with the general case of non-jumping cursors I think a modernized best practice example would be ideal, but that discussion still lives at #955.

I'd be totally fine with this issue being closed by assisting instead with the education of handling the general case. Though, this would still be a nice to have for the API, if possible.

DOM Stale Feature Request

Most helpful comment

I have isolated this feature on CodePen. It is possible to track the cursor just before the input re-renders with transformed values.

https://codepen.io/theoryofnekomata/pen/JVVPbq

Please observe how disabling the "make it happen" parts of the code makes the cursor jump to the end of the input.

All 7 comments

Thanks for the request @danielrob! It would be nice if the selection state didn't jump when the input isn't updated. We do track and restore cursor position, but not if the element didn't change after a render cycle. This is because we restore selection mainly to account for the case where an input may be moved to somewhere else in the DOM.

We could consider relaxing that condition and always restore selection state if it exists. I'm not sure what the downsides to that would be.

For now, here's a workaround for your specific example: https://codesandbox.io/s/mq7n0wk0vp

You can store and update the selection range manually using componentDidUpdate.

Faced the same issue. Any progress on it? Or may be any workaround of how we can call onChange without triggering setState?

According to me you would try with a

tags placed below the return( statement).

Is there any update on the same. I am still facing the issue. My state are manage via redux so there are no this.setState()

I have isolated this feature on CodePen. It is possible to track the cursor just before the input re-renders with transformed values.

https://codepen.io/theoryofnekomata/pen/JVVPbq

Please observe how disabling the "make it happen" parts of the code makes the cursor jump to the end of the input.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

Was this page helpful?
0 / 5 - 0 ratings