Solution for caret jumping in React inputs
What are we solving?
Let's say that we have a controled input and, on change, we are updating the input value by replacing
" " (spaces) with
The component looks like this:
Now, if we type in
Hello World, it will get formatted as
Let's say that we want it to say "Hello React World" instead. We position our cursor caret behind letter
o, and type
The problem is that as soon as we enter the first character, in this situation
<space>, the caret jumps to the end of the input value. If we just continued typing, input value would be
Hello--WorldReact instead of
Why does this happen?
The reason why this happens is because we are injecting a modified value in a DOM input (the one where we replace
" " with
-). When that happens, the input moves caret to the end.
This can be fixed by saving the caret position before changing the input value, and restoring it afterwards.
We can do that in two steps:
- Saving caret position (
selectionRange) to state.
- Updating input's
1. Saving caret position
First we need to add state for storing selectionRange and then, store it in
2. Updating selectionRange
Now, we need to restore caret position.
Firstly, add a ref to the input, and then use it to access input element and set it's selectionRange. To set the selectionRange, we will use the
useLayoutEffect hook from React.
useLayoutEffect is very similar to
useEffect. The difference is that
useLayoutEffect makes sure that any state updates or logic are processed before the browser repaints the screen. By using
useLayoutEffect instead of
useEffect, we are preventing any potential screen flickers.
That's it! Now the caret is no longer jumping to the end.