在 React 中处理数字输入可能会很痛苦,尤其是当您需要确保它们的大小正确或具有正确的小数位数时。简单的东西可能看起来很容易,但是一旦您深入了解本质并尝试实现自定义用户体验,代码可能会很快变得混乱。
我们大多数人使用的常见方法是编写自定义验证逻辑来限制 onChange 处理程序中的用户输入。
类似这样的
function NumberInput({ value, onChange, min, max, }: { value: number; onChange: (val: number) => void; min?: number; max?: number; }) { const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => { const val = +e.target.value; if (min != null && val < min) { onChange(min); return; } if (max != null && val > max) { onChange(max); return; } onChange(val); }; return ( <input type="number" value={value} onChange={changeHandler} /> ); }
这个逻辑乍一看似乎不错,但如果你已经尝试过了,你就会知道它会带来很多意想不到和奇怪的行为,而且根本不是一个好的用户体验!
另一种方法是使用标准 HTML 使用 min、max、maxLength 等属性进行内置验证的元素。但是,它缺乏我们通常想要实现的即时反馈和输入限制。
我们最终通过谷歌搜索 Stack Overflow 并找到了一些......黑客解决方案。
经过多次尝试和错误,我终于找到了更好的方法。
我们可以利用内置的 HTML 输入验证和一些自定义 JavaScript 来构建完美的解决方案。
这是组件
// Interface for props overriding the default value and onChange // attribute to accept only numeric value export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange"> { onChange: (val: number) => void; value: number; } function NumberInput({ value, onChange, min, max, step, ...props }: NumberInputProps) { // Internal input state to avoid weird behaviors with empty inputs const [input, setInput] = React.useState(value?.toString() || ""); const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => { // Using HTML input validity API to validation if ( (max != null && e.target.validity.rangeOverflow) || (min != null && e.target.validity.rangeUnderflow) || (step != null && e.target.validity.stepMismatch) ) return; const val = +e.target.value; setInput(e.target.value); onChange(val); }; // To ensure the external updates are reflected in the input element React.useEffect(() => { setInput(value.toString()); }, [value]); return ( <Input ref={ref} type="number" value={input} onChange={changeHandler} min={min} max={max} step={step} {...props} /> ); }
通过这种方法,我们可以利用内置的 HTML 验证,并限制无效的用户输入数字。
查看现场示例并尝试一下
我们可以通过将其提取到像这样的自定义挂钩中来使该逻辑更加可重用
export const useNumberInput = ({ value, onChange, min, max, step, }: { value: number; onChange: (val: number) => void; max?: number; min?: number; step?: number; }): InputHTMLAttributes<HTMLInputElement> => { const [input, setInput] = useState(value?.toString() || ""); const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => { if ( (max != null && e.target.validity.rangeOverflow) || (min != null && e.target.validity.rangeUnderflow) || (step != null && e.target.validity.stepMismatch) ) return; const val = +e.target.value; setInput(e.target.value); onChange(val); }; useEffect(() => { setInput(value.toString()); }, [value]); return { type: "number", value: input, onChange: changeHandler, min, max, step, }; };
并在任何需要的组件中使用它(显然有一个输入元素)。
export default function CustomInput() { const [value, setValue] = useState(0); const inputProps = useNumberInput({ value, onChange: setValue, min: 1, max: 50, }); return ( <div> <button onClick={() => onChange(value + 1)}> + </button> <button onClick={() => onChange(value - 1)}> - </button> <input {...inputProps} {...otherProps} /> </div> ); }
请随意添加评论并提出改进建议!
以上是在 React 中处理数字输入验证的最佳方法的详细内容。更多信息请关注PHP中文网其他相关文章!