Gérer les entrées de nombres dans React peut être pénible, surtout lorsque vous devez vous assurer qu'ils ont la bonne taille ou qu'ils ont le bon nombre de points décimaux. Des choses simples peuvent sembler faciles, mais une fois que vous entrez dans le vif du sujet et essayez de créer des expériences utilisateur personnalisées, le code peut rapidement devenir compliqué.
Une approche courante que la plupart d'entre nous utilisent consiste à écrire la logique de validation personnalisée qui restreint la saisie de l'utilisateur dans le gestionnaire onChange.
Quelque chose comme ça
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} /> ); }
Cette logique semble bonne à première vue, mais si vous l'avez déjà essayée, vous savez qu'elle s'accompagne de nombreux comportements inattendus et étranges et d'une expérience utilisateur pas du tout bonne !
Une autre approche consiste à utiliser le code HTML standard élément avec les validations intégrées utilisant des attributs tels que min, max, maxLength etc. Cependant, il lui manque le retour instantané et les restrictions de saisie que nous souhaitons habituellement implémenter.
Nous finissons par chercher sur Google notre chemin vers Stack Overflow et trouvons des solutions... hackish.
Après de nombreux essais et erreurs, j'ai finalement trouvé une meilleure façon de procéder.
Nous pouvons utiliser la validation d'entrée HTML intégrée avec du javascript personnalisé pour créer une solution parfaite.
Voici le composant
// 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} /> ); }
Avec cette approche, nous pouvons utiliser les validations HTML intégrées et également restreindre les saisies utilisateur non valides pour les nombres.
Découvrez un exemple en direct et jouez
Nous pouvons rendre cette logique plus réutilisable en l'extrayant dans un hook personnalisé comme celui-ci
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, }; };
Et utilisez-le dans n'importe quel composant si nécessaire (qui a évidemment un élément d'entrée).
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> ); }
N'hésitez pas à ajouter des commentaires et à suggérer des améliorations !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!