Many answers don't work in some scenarios and some introduce unneeded useEffect
while also not working in all cases.
The defaultValue
doesn't work for cases where the field is controlled, which is often needed when state is controlled and updated by upstream state.
In particular, when typing fast the timeout solutions don't work. (The answers with setTimeout
or useEffect
don't work when the user types quickly)
The Promise
solution in one answer works well, and it is simple and clean.
The minimal code for the Promise solution that works regardless of typing speed using a controlled component:
<input
value={value}
onChange={async (event) => {
const inputElement = event.target;
setValue(inputElement.value);
const { selectionStart, selectionEnd } = inputElement;
/**
* This works correctly, even if the user types quickly. (setTimeout does not)
*/
await Promise.resolve().then(() => {
inputElement.setSelectionRange(selectionStart, selectionEnd);
});
}}
/>
https://codesandbox.io/p/sandbox/qxg8k9?file=%2Fsrc%2FApp.js%3A1%2C1
For the original question, input type="number"
does not support setSelectionRange
, so many solutions to this question don't work if using that type
. The only one that can is defaultValue
, but that doesn't work if other code updates the state. Recommend using type="text"
and convert to number outside of form.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
The element must be of one of the following input types:
password
,search
,tel
,text
, orurl
.