Création de formulaires React en 2020

Bonjour, Habr. Je vous présente la traduction de l'article "Créer des formulaires dans React en 2020" par Kristofer Selbekk

image


Champs de saisie Zones de texte. Boutons radio et cases à cocher. Voici quelques-uns des principaux points d'interaction que nous, en tant que développeurs, avons avec nos utilisateurs. Nous les plaçons sur le site, les utilisateurs les remplissent et si nous avons de la chance, alors les formulaires remplis nous parviennent sans erreur de validation.

Le traitement des formulaires fait partie intégrante d'un grand nombre d'applications Web, et c'est l'une des choses que React fait le mieux. Vous avez une grande liberté pour créer et traiter des formulaires. Mais existe-t-il une meilleure façon de procéder?

Veuillez noter que dans tous ces exemples, nous allons créer un formulaire de connexion avec un champ e-mail et mot de passe, mais ces méthodes peuvent être utilisées avec la plupart des types de formulaires.

N'oubliez pas les règles de bonne forme


Bien que cela ne soit pas directement lié au sujet en discussion, je veux m'assurer que vous n'avez pas oublié de rendre vos formulaires compréhensibles pour tout le monde. Ajoutez des balises à vos champs de saisie, définissez les bons attributs aria pour les cas où la saisie n'est pas valide et structurez votre contenu sémantiquement correctement. Cela facilite l'utilisation de votre formulaire pour les utilisateurs et les développeurs.

Traitement des formulaires à l'aide du hook d'état


Pour commencer, voyons comment je travaille habituellement avec l'état du formulaire. J'enregistre tous les champs en tant qu'éléments d'état séparés et les mets à jour tous séparément, ce qui ressemble à ceci:

function LoginForm() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    api.login(email, password);
  };
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor='email'>Email</label>
        <input type='email' id='email' value={email} onChange={(e) => setEmail(e.target.value)} />
      </div>
      <div>
        <label htmlFor='password'>Password</label>
        <input type='password' id='password' value={password} onChange={(e) => setPassword(e.target.value)} />
      </div>
    </form>
  );
}

Tout d'abord, je crée deux parties distinctes de l'état - nom d'utilisateur et mot de passe. Ces deux variables sont ensuite transférées dans le champ de saisie correspondant, déterminant la valeur de ce champ. Chaque fois que quelque chose change dans un champ, nous sommes sûrs de mettre à jour la valeur de l'état, provoquant une refonte de notre application.

Ce traitement de formulaire fonctionne très bien dans la plupart des cas et est simple et facile à utiliser et à comprendre. Cependant, il est assez fastidieux d'écrire une telle construction à chaque fois.

Création d'un crochet personnalisé


Faisons un peu de refactoring et créons notre propre hook, ce qui améliorera légèrement notre code:

const useFormField = (initialValue: string = '') => {
  const [value, setValue] = React.useState(initialValue);
  const onChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value), []);
  return { value, onChange };
};

export function LoginForm() {
  const emailField = useFormField();
  const passwordField = useFormField();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    api.login(emailField.value, passwordField.value);
  };
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor='email'>Email</label>
        <input type='email' id='email' {...emailField} />
      </div>
      <div>
        <label htmlFor='password'>Password</label>
        <input type='password' id='password' {...passwordField} />
      </div>
    </form>
  );
}

Nous créons un hook useFormField personnalisé qui crée pour nous un gestionnaire d'événements onChange et stocke également la valeur dans l'état du formulaire. Dans ce cas, nous pouvons utiliser notre hook avec tous les champs du formulaire.

Traitement d'un grand nombre de champs


L'inconvénient de cette approche est qu'elle ne s'adapte pas bien à mesure que votre forme se développe. Pour les petits formulaires comme la connexion, c'est probablement bon, mais lorsque vous créez des formulaires de profil utilisateur, vous devrez peut-être demander beaucoup d'informations! Faut-il appeler notre crochet encore et encore?

Chaque fois que je rencontre une situation similaire, j'ai tendance à écrire un crochet personnalisé qui stocke tous mes formulaires dans un grand fragment. Cela pourrait ressembler à ceci:

export function LoginForm() {
  const { formFields, createChangeHandler } = useFormFields({
    email: '',
    password: '',
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    api.login(formFields.email, formFields.password);
  };
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor='email'>Email</label>
        <input type='email' id='email' value={formFields.email} onChange={createChangeHandler('email')} />
      </div>
      <div>
        <label htmlFor='password'>Password</label>
        <input type='password' id='password' value={formFields.password} onChange={createChangeHandler('password')} />
      </div>
    </form>
  );
}

Avec le hook useFormFields, nous pouvons continuer à ajouter des champs sans ajouter de complexité à notre composant. Nous pouvons accéder à tous les états de formulaire en un seul endroit, et notre code semble soigné et concis. Bien sûr, vous devrez probablement utiliser setState pour certaines situations, mais pour la plupart des formulaires, la technique ci-dessus est très bien.

Approche alternative


Ainsi, la gestion des états fonctionne correctement, et dans la plupart des cas, c'est l'approche préférée dans React. Mais saviez-vous qu'il existe un autre moyen? Il s'avère que le navigateur traite par défaut l'état interne du formulaire, et nous pouvons l'utiliser pour simplifier notre code!

Voici le même formulaire, mais permettant au navigateur de gérer l'état du formulaire:

export function LoginForm() {
  const handleSubmit = (e: React.FormEvent) => {
	e.preventDefault();
	const formData = new FormData(e.target as HTMLFormElement);
	api.login(formData.get('email'), formData.get('password'));
  };
  return (
	<form onSubmit={handleSubmit}>
  	<div>
    	<label htmlFor="email">Email</label>
    	<input
      	type="email"
      	id="email"
      	name="email"
    	/>
  	</div>
  	<div>
    	<label htmlFor="password">Password</label>
    	<input
      	type="password"
      	id="password"
      	name="password"
    	/>
  	</div>
  	<button>Log in</button>
	</form>
  );
}

Regardez comme c'est facile! Aucun crochet n'a été utilisé, il n'y a pas de paramètre de valeur initiale, il n'y a pas de gestionnaire onChange. La meilleure partie est que le code fonctionne toujours comme avant - mais comment?

Vous avez peut-être remarqué que nous faisons quelque chose d'un peu différent dans la fonction handleSubmit. Nous utilisons l'API de navigateur intégrée appelée FormData. FormData est un moyen pratique (et bien pris en charge) d'obtenir des valeurs de nos champs de saisie!

Nous obtenons un lien vers le formulaire dans le DOM via l'attribut cible de l'événement submit et créons une nouvelle instance de la classe FormData. Maintenant, nous pouvons obtenir tous les champs par leur attribut de nom en appelant formData.get ("nom du champ d'entrée").

De cette façon, vous n'avez jamais besoin de gérer explicitement l'état du formulaire. Si vous avez besoin de valeurs par défaut (par exemple, si vous modifiez les valeurs de champ initiales à partir d'une base de données ou d'un stockage local), React vous propose une option defaultValue pratique pour cela.

Quand utiliser chaque approche


Étant donné que les formulaires font partie intégrante de la plupart des applications Web, il est important de savoir comment les gérer. Et React vous offre de nombreuses façons de procéder.
Pour les formulaires simples qui ne nécessitent pas de validation rigoureuse (ou qui peuvent s'appuyer sur des contrôles de validation de formulaire HTML5 ), je vous suggère simplement d'utiliser la gestion d'état intégrée que le DOM nous fournit par défaut. Il y a un certain nombre de choses que vous ne pouvez pas faire (par exemple, modifier par programmation les valeurs d'entrée ou une vérification en temps réel), mais dans les cas les plus simples (par exemple, un champ de recherche ou un champ de connexion, comme indiqué ci-dessus), vous le trouverez probablement approprié notre approche alternative en utilisant l'API du navigateur.

Lorsque vous effectuez une vérification personnalisée ou que vous avez besoin d'accéder à certaines données de formulaire avant de les soumettre, vous devez traiter explicitement l'état à l'aide de composants contrôlés. Vous pouvez utiliser régulièrement useStateHooks ou créer un hook personnalisé pour simplifier un peu votre code.

Il convient de noter que les développeurs de React recommandent l'utilisation de composants contrôlés (gèrent explicitement l'état du composant) dans la plupart des cas - car cette approche vous donne plus de flexibilité à l'avenir. À mon avis, les développeurs sacrifient souvent la simplicité au profit de la flexibilité, dont ils n'ont souvent pas besoin.

Quelle que soit l'approche que vous décidez d'utiliser, le traitement des formulaires React n'a jamais été aussi simple qu'aujourd'hui. Vous pouvez laisser le navigateur gérer des formulaires simples, tout en traitant explicitement l'état des formulaires lorsque la situation l'exige. J'espère que les méthodes ci-dessus seront utiles et vous aideront à résoudre le problème en réduisant le nombre de lignes dans votre code.

All Articles