Cours React Native · Module 5: Formulaires

React Hook Form - Des formulaires professionnels

3 min de lecture ·
Dans cette leçon4

React Hook Form est la bibliothèque de formulaires la plus populaire - efficace et facile à utiliser !

Installation

npx expo install react-hook-form

Formulaire de base

import { useForm, Controller } from 'react-hook-form';

interface FormData {
  name: string;
  email: string;
  password: string;
}

function RegisterForm() {
  const {
    control,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<FormData>({
    defaultValues: {
      name: '',
      email: '',
      password: '',
    },
  });

  const onSubmit = async (data: FormData) => {
    console.log('Données du formulaire :', data);
    // Envoyer au serveur
  };

  return (
    <View style={styles.container}>
      <Controller
        control={control}
        name="name"
        rules={{
          required: 'Le nom est obligatoire',
          minLength: { value: 2, message: 'Min. 2 caractères' },
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={[styles.input, errors.name && styles.inputError]}
            placeholder="Nom"
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
      />
      {errors.name && (
        <Text style={styles.error}>{errors.name.message}</Text>
      )}

      <Controller
        control={control}
        name="email"
        rules={{
          required: 'L\'email est obligatoire',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: 'Email invalide',
          },
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={[styles.input, errors.email && styles.inputError]}
            placeholder="Email"
            keyboardType="email-address"
            autoCapitalize="none"
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
      />
      {errors.email && (
        <Text style={styles.error}>{errors.email.message}</Text>
      )}

      <Pressable
        style={styles.button}
        onPress={handleSubmit(onSubmit)}
        disabled={isSubmitting}
      >
        <Text style={styles.buttonText}>
          {isSubmitting ? 'Envoi...' : 'S\'inscrire'}
        </Text>
      </Pressable>
    </View>
  );
}

Composant Input personnalisé

interface FormInputProps {
  control: any;
  name: string;
  rules?: object;
  placeholder?: string;
  secureTextEntry?: boolean;
  keyboardType?: KeyboardTypeOptions;
}

function FormInput({
  control,
  name,
  rules,
  placeholder,
  secureTextEntry,
  keyboardType,
}: FormInputProps) {
  return (
    <Controller
      control={control}
      name={name}
      rules={rules}
      render={({
        field: { onChange, onBlur, value },
        fieldState: { error },
      }) => (
        <View style={styles.inputContainer}>
          <TextInput
            style={[styles.input, error && styles.inputError]}
            placeholder={placeholder}
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
            secureTextEntry={secureTextEntry}
            keyboardType={keyboardType}
          />
          {error && <Text style={styles.error}>{error.message}</Text>}
        </View>
      )}
    />
  );
}

// Utilisation
function CleanForm() {
  const { control, handleSubmit } = useForm();

  return (
    <View>
      <FormInput
        control={control}
        name="name"
        placeholder="Nom"
        rules={{ required: 'Nom obligatoire' }}
      />
      <FormInput
        control={control}
        name="email"
        placeholder="Email"
        keyboardType="email-address"
        rules={{
          required: 'Email obligatoire',
          pattern: { value: /\S+@\S+\.\S+/, message: 'Email invalide' },
        }}
      />
    </View>
  );
}

Watch et setValue

function DynamicForm() {
  const { control, watch, setValue } = useForm({
    defaultValues: { country: '', city: '' },
  });

  const country = watch('country');

  useEffect(() => {
    // Réinitialiser la ville quand le pays change
    setValue('city', '');
  }, [country]);

  return (
    <View>
      <FormInput control={control} name="country" placeholder="Pays" />
      <FormInput control={control} name="city" placeholder="Ville" />
      <Text>Pays sélectionné : {country}</Text>
    </View>
  );
}

React Hook Form minimise les re-rendus et offre un excellent support de TypeScript !

Code de cette leçon : App.tsx
import React from 'react';
import { View, Text, TextInput, Pressable, StyleSheet } from 'react-native';
import { useForm, Controller } from 'react-hook-form';

interface FormData {
  email: string;
}

export default function App() {
  // TODO : récupère control, handleSubmit et formState: { errors } depuis useForm<FormData>()
  // transmets defaultValues: { email: '' }

  const onSubmit = (data: FormData) => {
    console.log('Form data:', data);
  };

  return (
    <View style={styles.container}>
      {/* TODO : ajoute un <Controller> qui :
          - prend control={control}
          - a name="email"
          - a rules={{ required: 'Email requis' }}
          - dans render renvoie un <TextInput> avec onChangeText={onChange}, onBlur={onBlur}, value={value} */}

      {/* TODO : sous le champ, affiche l'erreur : errors.email && <Text style={styles.error}>{errors.email.message}</Text> */}

      <Pressable style={styles.button} onPress={handleSubmit(onSubmit)}>
        <Text style={styles.buttonText}>Valider</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#1a5276',
  },
  input: {
    backgroundColor: '#fff',
    borderRadius: 8,
    padding: 14,
    fontSize: 16,
    color: '#1a5276',
    marginBottom: 8,
  },
  error: {
    color: '#e74c3c',
    marginBottom: 12,
  },
  button: {
    backgroundColor: '#2980b9',
    borderRadius: 8,
    padding: 14,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
});

Entrées de la documentation utilisées dans cette leçon

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Que retourne le hook useForm de react-hook-form ?

  2. 2. Comment ajoute-t-on la validation 'required' dans Controller ?

Tâches pratiques dans le jeu

  • Tri vertical

    Ordonne les éléments de Controller de l'extérieur vers l'intérieur :

  • Éditeur de code

    Complète le code avec useForm et Controller.

Articles utiles