我有一个值的输入。我想验证用户输入的新值,并防止该值在无效时出现在输入中。例如,值应该是数字,因此只能包含数字、点和逗号。
我有一个验证函数,它返回true/false取决于一个值是否符合标准(按预期工作):
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 }
在我的输入字段中,我调用了一个自定义handleChange函数,以确保新输入在将其存储到状态变量之前是有效的。
handleChange
渲染输入
<input value={inputVal} onChange={handleChange} />
处理输入变化
function handleChange(e) { const value = e.target.value if(validateInput(value)) { setInputVal(value) } }
如果插入了无效输入并且验证失败,则我的输入字段中的值不会更改(如预期的那样),但插入符号会跳转到输入字段的末尾(不可取)。
尝试的解决方案
我试图通过采用这个答案来修复插入符号的跳跃行为,其中我创建了一个cursor状态变量来跟踪预期的插入符号位置:
cursor
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} /> ) }
使用此解决方案,插入符号在第一次输入无效值时保持在正确的位置,但是当我继续按下相同的键时,一遍又一遍地插入无效值,在第一次之后,插入符号移回末尾的输入。这是因为 useEffect 没有被调用第二次和后续的时间。
无论用户尝试插入无效值多少次,我怎样才能使这项工作使插入符号始终保持在同一位置?
我所说的也是正确的,但在当前情况下并非如此。请在下面找到更新的代码。下面的代码也应该更高效,因为它不涉及每次更改的重新渲染
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> ); }
我也在播放代码代码链接上进行了相同的更改
React 在 setstate 调用时重新渲染整个组件。在您的情况下,值将被保留,因为它是受控输入。但在幕后 React 基本上是在每次输入值时创建一个新的输入字段。强制做出反应以使用具有恒定值的相同字段传递键道具。
<input key='MyFixedKeyValue' ref={inputRef} value={inputVal} onChange={handleChange} />