[英]Caret Jumping to End When Rejecting Input
I have an input for a value.我有一个值的输入。 I want to validate the new value entered by the user and prevent the value from appearing in the input if it's invalid.
我想验证用户输入的新值,并防止该值在无效时出现在输入中。 For instance, the value should be numeric, and hence can only contain numbers, dots, and commas.
例如,值应该是数字,因此只能包含数字、点和逗号。
I have a validation function which returns true
/ false
depending on whether a value meets the criteria (works as intended):我有一个验证 function 根据值是否符合标准(按预期工作)返回
true
/ false
:
function validateInput(input) {
let allow = true
const splitValuesByDecimal = input.split(',')
if(allow) allow = /^[\d|,|.]*?$/.test(input) // allow only digits, dots, and commas
if(allow) allow = splitValuesByDecimal.length <= 2 // allow only 1 decimal separator (comma)
if(allow && splitValuesByDecimal.length > 1) allow = splitValuesByDecimal.at(-1).length <= 2 // allow only maximum of 2 decimal characters
return allow
}
In my input field, I call a custom handleChange
function which ensures that the new input is valid before storing it in the state variable.在我的输入字段中,我调用了一个自定义的
handleChange
function,它确保新输入在存储到 state 变量之前是有效的。
Rendering input渲染输入
<input value={inputVal} onChange={handleChange} />
Handling input change处理输入变化
function handleChange(e) {
const value = e.target.value
if(validateInput(value)) {
setInputVal(value)
}
}
If an invalid input is inserted and validation fails, the value in my input field does not change (as intended), but the caret jumps to the end of the input field (not desirable).如果插入了无效输入并且验证失败,则我的输入字段中的值不会更改(如预期的那样),但插入符号会跳转到输入字段的末尾(不可取)。
Attempted Solution尝试的解决方案
I have tried to fix the caret jumping behaviour by adopting this answer where I created a cursor
state variable which keeps track of the intended caret position:我试图通过采用这个答案来修复插入符号跳跃行为,其中我创建了一个
cursor
state 变量,该变量跟踪预期的插入符号 position:
function MyComponent() {
const [inputVal, setInputVal] = useState('')
const [cursor, setCursor] = useState(null)
const inputRef = useRef()
useEffect(() => {
if (inputRef.current) inputRef.current.setSelectionRange(cursor, cursor);
}, [inputRef, cursor, inputVal])
function validateInput(input) {
...
}
function handleChange(e) {
const value = e.target.value
if(validateInput(value)) {
setCursor(e.target.selectionStart)
setInputVal(value)
}
else {
setCursor(e.target.selectionStart - 1)
}
}
return (
<input
ref={inputRef}
value={inputVal}
onChange={handleChange} />
)
}
With this solution, the caret persists at the correct position the first time an invalid value is entered, but as I keep on subsequently pressing the same key inserting invalid value over and over again, after the first time, the caret moves back to the end of the input.使用此解决方案,插入符号在第一次输入无效值时保持在正确的 position 处,但随着我继续按下相同的键,一遍又一遍地插入无效值,在第一次之后,插入符号移回末尾的输入。 This is because the useEffect is not called the second and subsequent times.
这是因为 useEffect 没有被调用第二次和后续的时间。
How can I make this work so that caret always stays at the same position regardless of how many times the user attempts to insert an invalid value?无论用户尝试插入无效值多少次,我如何才能使插入符号始终保持在相同的 position 上?
Update:-更新:-
Sorry my bad, I haven't looked at the question completely.对不起,我的不好,我没有完全看这个问题。 However what I was saying is also correct but not in current case.
但是,我所说的也是正确的,但在当前情况下并非如此。 Please find the updated code below.
请在下面找到更新的代码。 Below code should also be more performant as it does not involve re-renders on every change
下面的代码也应该更高效,因为它不涉及每次更改的重新渲染
import React, { useState, useRef } from 'react';
export function App(props) {
const inputRef = useRef()
const oldVal = useRef(null)
function validateInput(input) {
let allow = true
const splitValuesByDecimal = input.split(',')
if(allow) allow = /^[\d|,|.]*?$/.test(input) // allow only digits, dots, and commas
if(allow) allow = splitValuesByDecimal.length <= 2 // allow only 1 decimal separator (comma)
if(allow && splitValuesByDecimal.length > 1) allow = splitValuesByDecimal.at(-1).length <= 2 // allow only maximum of 2 decimal characters
console.log(allow)
return allow
}
function handleChange(e) {
const valueNew = e.target.value
const pos = e.target.selectionStart - 1
if(validateInput(valueNew)) {
oldVal.current = valueNew
} else {
inputRef.current.value = oldVal.current
inputRef.current.setSelectionRange(pos, pos)
}
}
return (
<div key='mydiv' className='App'>
<input key='MyFixedKeyValue' ref={inputRef} onChange={handleChange} />
</div>
);
}
I also changed the same on playcode code link我也在播放代码代码链接上进行了相同的更改
React re-renders the whole component on setstate call. React 在 setstate 调用时重新渲染整个组件。 In your case values are being kept as it is a controlled input.
在您的情况下,值将被保留,因为它是受控输入。 But behind the scene react is basically creating a new input field every time you are entering a value.
但在幕后 React 基本上是在每次输入值时创建一个新的输入字段。 To force react to use the same field pass key prop with constant value.
强制做出反应以使用具有恒定值的相同字段传递键道具。
<input
key='MyFixedKeyValue'
ref={inputRef}
value={inputVal}
onChange={handleChange} />
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.