JavaRush /Курсы /Модуль 3: React /Типизация полей формы и их состояния с TypeScript

Типизация полей формы и их состояния с TypeScript

Модуль 3: React
5 уровень , 3 лекция
Открыта

Определение интерфейса для данных формы

Начнём с создания интерфейса для данных нашей формы. Представьте, что мы делаем форму для регистрации со следующими полями:

  • name (строка)
  • email (строка)
  • password (строка)

Вот как мы можем описать данные формы:

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

Просто? Да. Полезно? Очень!

Интеграция интерфейса с Formik

Теперь, когда у нас есть интерфейс, давайте используем его в компоненте Formik. Мы можем передать его через generic тип в компонент <Formik>.

import React from 'react';
import { Formik, Form, Field } from 'formik';

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

const initialValues: RegistrationFormValues = {
  name: '',
  email: '',
  password: '',
};

const RegistrationForm: React.FC = () => {
  return (
    <Formik<RegistrationFormValues>
      initialValues={initialValues}
      onSubmit={(values) => {
        console.log('Form Data:', values);
      }}
    >
      {() => (
        <Form>
          <div>
            <label htmlFor="name">Name</label>
            <Field id="name" name="name" placeholder="Enter your name" />
          </div>
          <div>
            <label htmlFor="email">Email</label>
            <Field id="email" name="email" type="email" placeholder="Enter your email" />
          </div>
          <div>
            <label htmlFor="password">Password</label>
            <Field id="password" name="password" type="password" placeholder="Enter your password" />
          </div>
          <button type="submit">Register</button>
        </Form>
      )}
      </Formik>
      );
};

export default RegistrationForm;

Что здесь происходит?

  1. Мы передали интерфейс RegistrationFormValues через <Formik<RegistrationFormValues>>. Это позволяет Formik знать, какие поля присутствуют в нашей форме.

  2. initialValues строго типизированы. Если вы забудете добавить какое-либо поле или сделаете опечатку, TypeScript сразу выстрелит в вас ошибкой.

  3. Поля формы <Field>, такие как name, типизированы автоматически благодаря привязке к интерфейсу.

Типизация функции onSubmit

Без типизации функции onSubmit можно ненароком забыть, какие данные мы ожидаем. Хорошая новость состоит в том, что Formik уже знает, какие данные ваша форма должна возвращать!

const RegistrationForm: React.FC = () => {
  const handleSubmit = (values: RegistrationFormValues) => {
    // TypeScript знает, что values соответствует RegistrationFormValues
    console.log('Form Submitted:', values);
  };

  return (
    <Formik<RegistrationFormValues>
      initialValues={initialValues}
      onSubmit={handleSubmit}
    >
      {/* ... */}
      </Formik>
      );
};

Теперь, если вы вдруг передадите неправильный интерфейс, TypeScript даст вам об этом знать.

Типизация <Field> и <ErrorMessage>

Formik предоставляет отличные встроенные компоненты, такие как <Field> и <ErrorMessage>. Их тоже можно типизировать!

<Field<RegistrationFormValues['name']>
  id="name"
  name="name"
  placeholder="Enter your name"
/>

Здесь мы указали, что этот <Field> соответствует полю name из интерфейса RegistrationFormValues.

Аналогично можно работать с <ErrorMessage>:

import { ErrorMessage } from 'formik';

<ErrorMessage name="name" render={(msg) => <div className="error">{msg}</div>} />

Компоненты пользовательских полей

Formik позволяет использовать кастомные компоненты вместо <Field>. Давайте создадим кастомное поле для ввода текста:

interface CustomInputProps {
  field: {
    name: string;
    value: string;
    onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
    onBlur: (e: React.FocusEvent<HTMLInputElement>) => void;
  };
  form: {
    touched: Record<string, boolean>;
    errors: Record<string, string>;
  };
}

const CustomInput: React.FC<CustomInputProps> = ({ field, form }) => {
  return (
    <div>
      <input {...field} />
      {form.touched[field.name] && form.errors[field.name] && (
        <div className="error">{form.errors[field.name]}</div>
      )}
    </div>
  );
};

Использование этого компонента:

<Field name="name" component={CustomInput} />

Зачем это нужно?

Использование кастомных полей полезно, если вам нужно сложное поведение или специфические элементы ввода (например, выпадающий список или редактор текста).

Обработка сложных форм (объекты и массивы)

Что если наша форма становится более сложной? Представьте, что мы добавляем динамический массив полей, например, для адресов пользователя:

interface Address {
  street: string;
  city: string;
}

interface UserFormValues {
  name: string;
  addresses: Address[];
}

Formik поддерживает работу с объектами и массивами через FieldArray. Вот пример:

import { FieldArray } from 'formik';

const initialValues: UserFormValues = {
  name: '',
  addresses: [{ street: '', city: '' }],
};

<FieldArray name="addresses">
  {({ remove, push }) => (
    <>
      {values.addresses.map((_, index) => (
        <div key={index}>
          <Field name={`addresses[${index}].street`} placeholder="Street" />
          <Field name={`addresses[${index}].city`} placeholder="City" />
          <button type="button" onClick={() => remove(index)}>Remove</button>
        </div>
      ))}
      <button type="button" onClick={() => push({ street: '', city: '' })}>
        Add Address
  </button>
    </>
  )}
    </FieldArray>

Здесь мы добавили поддержку динамического добавления и удаления адресов.

Типичные ошибки и как их избежать

Если вы пропустите какой-либо из ключей интерфейса в initialValues, TypeScript сразу предупредит вас. Но если вы используете тип any, Formik не сможет выгнать вас с работы за ошибки — избегайте его. Ещё одна частая ошибка — забыть передать generic тип в <Formik> и <Field>: это приведёт к потере всех плюсов типизации.

TypeScript иногда может ругаться, что Field не передаёт правильный тип. Если это произошло, проверьте, чтобы имя поля совпадало с ключом интерфейса.

1
Задача
Модуль 3: React, 5 уровень, 3 лекция
Недоступна
Создание интерфейса для формы
Создание интерфейса для формы
1
Задача
Модуль 3: React, 5 уровень, 3 лекция
Недоступна
Добавление формы с полями
Добавление формы с полями
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ