Kurs React Native · Moduł 5: Formularze

React Hook Form - Profesjonalne formularze

2 min czytania ·
W tej lekcji4

React Hook Form to najpopularniejsza biblioteka do formularzy - wydajna i łatwa w użyciu!

Instalacja

npx expo install react-hook-form

Podstawowy formularz

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('Form data:', data);
    // Wysłanie na serwer
  };

  return (
    <View style={styles.container}>
      <Controller
        control={control}
        name="name"
        rules={{
          required: 'Imię jest wymagane',
          minLength: { value: 2, message: 'Min. 2 znaki' },
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={[styles.input, errors.name && styles.inputError]}
            placeholder="Imię"
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
      />
      {errors.name && (
        <Text style={styles.error}>{errors.name.message}</Text>
      )}

      <Controller
        control={control}
        name="email"
        rules={{
          required: 'Email jest wymagany',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: 'Nieprawidłowy email',
          },
        }}
        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 ? 'Wysyłanie...' : 'Zarejestruj'}
        </Text>
      </Pressable>
    </View>
  );
}

Custom Input Component

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>
      )}
    />
  );
}

// Użycie
function CleanForm() {
  const { control, handleSubmit } = useForm();

  return (
    <View>
      <FormInput
        control={control}
        name="name"
        placeholder="Imię"
        rules={{ required: 'Imię wymagane' }}
      />
      <FormInput
        control={control}
        name="email"
        placeholder="Email"
        keyboardType="email-address"
        rules={{
          required: 'Email wymagany',
          pattern: { value: /\S+@\S+\.\S+/, message: 'Nieprawidłowy email' },
        }}
      />
    </View>
  );
}

Watch i setValue

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

  const country = watch('country');

  useEffect(() => {
    // Reset miasta przy zmianie kraju
    setValue('city', '');
  }, [country]);

  return (
    <View>
      <FormInput control={control} name="country" placeholder="Kraj" />
      <FormInput control={control} name="city" placeholder="Miasto" />
      <Text>Wybrany kraj: {country}</Text>
    </View>
  );
}

React Hook Form minimalizuje re-rendery i oferuje świetne TypeScript support!

Kod do tej lekcji: 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: pobierz control, handleSubmit oraz formState: { errors } z useForm<FormData>()
  // przekaż defaultValues: { email: '' }

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

  return (
    <View style={styles.container}>
      {/* TODO: dodaj <Controller> który:
          - przyjmuje control={control}
          - ma name="email"
          - ma rules={{ required: 'Email jest wymagany' }}
          - w render zwraca <TextInput> z onChangeText={onChange}, onBlur={onBlur}, value={value} */}

      {/* TODO: pod polem wyświetl błąd: errors.email && <Text style={styles.error}>{errors.email.message}</Text> */}

      <Pressable style={styles.button} onPress={handleSubmit(onSubmit)}>
        <Text style={styles.buttonText}>Wyślij</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',
  },
});

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co zwraca hook useForm z react-hook-form?

  2. 2. Jak dodać walidację 'required' w Controller?

Zadania praktyczne w grze

  • Układanie w pionie

    Uporządkuj elementy Controller od zewnętrznych:

  • Edytor kodu

    Uzupełnij kod z useForm i Controller.

Przydatne artykuły