一尘不染

拒绝输入时插入符号跳到结尾

javascript

我有一个值的输入。我想验证用户输入的新值,并防止该值在无效时出现在输入中。例如,值应该是数字,因此只能包含数字、点和逗号。

我有一个验证函数,它返回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函数,以确保新输入在将其存储到状态变量之前是有效的。

渲染输入

<input value={inputVal} onChange={handleChange} />

处理输入变化

function handleChange(e) {
    const value = e.target.value
    if(validateInput(value)) {
        setInputVal(value)
    }
}

如果插入了无效输入并且验证失败,则我的输入字段中的值不会更改(如预期的那样),但插入符号会跳转到输入字段的末尾(不可取)。


尝试的解决方案

我试图通过采用这个答案来修复插入符号的跳跃行为,其中我创建了一个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 没有被调用第二次和后续的时间。

无论用户尝试插入无效值多少次,我怎样才能使这项工作使插入符号始终保持在同一位置?


阅读 91

收藏
2022-07-26

共1个答案

一尘不染

我所说的也是正确的,但在当前情况下并非如此。请在下面找到更新的代码。下面的代码也应该更高效,因为它不涉及每次更改的重新渲染

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} />
2022-07-26