31

The cursor keeps going to the end. How to keep the cursor position when editing from the the middle of the string?

enter image description here

Code that I am using is:

const rootElement = document.getElementById('root');

class MyFancyForm extends React.Component {

    constructor(props) {
        super(props);
        this.state = {myValue: ""};
    }

    handleCommaSeparatedChange = event => {
        const {value} = event.target;
        this.setState({myValue: value});
    };




    render() {
        return(
            <form >

                <div>
                    <label>
                        Cursor position looser
                        <br />
                        <input onChange={this.handleCommaSeparatedChange} value={this.state.myValue} />
                    </label>
                </div>


            </form>
        )
    }
}

const element = <MyFancyForm />;

ReactDOM.render(element, rootElement);

Any idea how could I achieve it?

12
  • I tried your exact code and I don't get this jumpy cursor behavior. What browser is this? Commented Feb 18, 2019 at 10:58
  • 1
    @Josep I actually do not see the problem at codesandbox.io/s/km35znxvx5 although i see it at the jsfiddle you posted. Could it be a bundler/version issue ? Commented Feb 18, 2019 at 11:46
  • 1
    I'm getting crazy about how here it's wrong but here is ok. Commented Feb 18, 2019 at 11:54
  • 1
    @Vencovsky well it's not the browser, or the provided code, that means the only thing we are left with is versioning of the libraries. i think Gabriele Petrioli is on to something. Commented Feb 18, 2019 at 12:02
  • 1
    @redochka where were you running this code on? Your local environment or something like codepen or jsfiddle? Commented Feb 18, 2019 at 12:06

3 Answers 3

17

just change value into defaultValue - it worked both in codepen and codesandbox for me

class MyFancyForm extends React.Component {

    constructor(props) {
        super(props);
        this.state = {myValue: ""};
    }

    handleCommaSeparatedChange = event => {
        const {value} = event.target;
        this.setState({myValue: value});
    };




    render() {
        return(
            <form >

                <div>
                    <label>
                        Cursor position looser
                        <br />
                        <input onChange={this.handleCommaSeparatedChange} defaultValue={this.state.myValue} />
                    </label>
                </div>


            </form>
        )
    }
}


  ReactDOM.render(
    <MyFancyForm />,
    document.getElementById('root')
  );

Sign up to request clarification or add additional context in comments.

2 Comments

Does not work when we are formatting the value by usint 'onChange' event.
This doesn't work if you change the value in any other way besides typing into the text box. The defaultValue sets the initial value but if you later change that value (besides typing) it doesn't get updated.
4

I know it's an old post but this might help someone.

I found this snippet in one github issue and helped me to get around this problem.

onChange={(event) => {
  event.persist()
  const caretStart = event.target.selectionStart;
  const caretEnd = event.target.selectionEnd;
  // update the state and reset the caret
  this.updateState();
  event.target.setSelectionRange(caretStart, caretEnd);
}}

Quote from: https://github.com/facebook/react/issues/955#issuecomment-469344232

1 Comment

The only problem is that setSelectionRange doesn't work on all input types. For example type=number or email.
4

I solved this by creating a TextInput component that wraps <input type="text"> and proxying the value in internal state.

function TextInput({ value, onChange }) {
    // Create a proxy value in internal state to prevent the caret from jumping to the end every time the value updates
    const [currentValue, setCurrentValue] = useState<string>(value);

    useEffect(() => {
        setCurrentValue(value);
    }, [value]);

    return (<input
                type="text"
                value={currentValue}
                onChange={(e) => {
                    setCurrentValue(e.target.value);

                    onChange(e.target.value);
                }}
            />);
}

Then I use it in a parent component like so. It works well so far.

<TextInput                  
    value={textValue}
    onChange={(e) => {
        setTextValue(e);
    }}
/>

1 Comment

It would be nice if you could explain why this works!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.