JavaRush /Курсы /Модуль 3: React /Создание многошаговой формы с валидацией на каждом этапе

Создание многошаговой формы с валидацией на каждом этапе

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

Введение

Многошаговые формы часто используются, когда пользователю нужно заполнить длинную форму, но разработчики (как мы с вами) хотят улучшить его опыт. Разделив форму на несколько шагов, мы убираем ощущение перегрузки от множества полей. Каждому шагу даётся своё время и внимание, а мы можем валидировать данные поэтапно, чтобы минимизировать ошибки в дальнейшем.

Классический пример: форма регистрации на сайте, где на первом шаге заполняются личные данные, на втором — адрес, а на третьем — платёжная информация.

Структура многошаговой формы

Наша многошаговая форма будет состоять из двух основных частей:

  1. Навигация между шагами, где мы контролируем текущий шаг и управляем кнопками "Далее" и "Назад".
  2. Форма для данных текущего шага с отдельной схемой валидации для каждого этапа.

Для реализации мы будем использовать:

  • Formik — для управления состоянием формы и её полями.
  • Yup — для валидации данных.
  • React — для управления логикой переключения шагов.

Создание структуры компонента

1. Создаём базовую структуру

Сначала мы построим скелет многошаговой формы. Здесь мы добавим логику для переходов между шагами:

import React, { useState } from 'react';

const MultiStepForm = () => {
  // Текущее состояние шага
  const [currentStep, setCurrentStep] = useState<number>(0);

  // Данные шагов (для простоты — массив строк)
  const steps = ['Личные данные', 'Адрес', 'Оплата'];

  // Функция перехода на следующий шаг
  const nextStep = () => {
    if (currentStep < steps.length - 1) {
      setCurrentStep(currentStep + 1);
    }
  };

  // Функция возврата на предыдущий шаг
  const prevStep = () => {
    if (currentStep > 0) {
      setCurrentStep(currentStep - 1);
    }
  };

  return (
    <div>
      <h1>{steps[currentStep]}</h1>
      <div>
        <button onClick={prevStep} disabled={currentStep === 0}>
          Назад
        </button>
        <button onClick={nextStep} disabled={currentStep === steps.length - 1}>
          Далее
        </button>
      </div>
    </div>
  );
};

export default MultiStepForm;

Сначала это базовый скелет. Форма отображает текущий шаг с кнопками "Назад" и "Далее". Управление осуществляется через состояние currentStep.

2. Добавляем формы для каждого шага

Теперь добавим формы для каждого шага. Создадим их как отдельные компоненты:

// Шаг 1: Форма для личных данных
const StepOne = () => (
  <div>
    <label>Имя</label>
    <input type="text" name="name" />
    <label>Email</label>
    <input type="email" name="email" />
  </div>
);

// Шаг 2: Форма для адреса
const StepTwo = () => (
  <div>
    <label>Город</label>
    <input type="text" name="city" />
    <label>Улица</label>
    <input type="text" name="street" />
  </div>
);

// Шаг 3: Форма для оплаты
const StepThree = () => (
  <div>
    <label>Номер карты</label>
    <input type="text" name="cardNumber" />
    <label>Дата окончания</label>
    <input type="text" name="expiryDate" />
  </div>
);

Теперь добавим их в наш компонент:

const renderStep = (step: number) => {
  switch (step) {
    case 0:
      return <StepOne />;
    case 1:
      return <StepTwo />;
    case 2:
      return <StepThree />;
    default:
      return null;
  }
};

const MultiStepForm = () => {
  // Логика шагов остаётся прежней
  return (
    <div>
      <h1>{steps[currentStep]}</h1>
      {renderStep(currentStep)}
      <div>
        <button onClick={prevStep} disabled={currentStep === 0}>
          Назад
        </button>
        <button onClick={nextStep} disabled={currentStep === steps.length - 1}>
          Далее
        </button>
      </div>
    </div>
  );
};

Добавляем состояние формы с Formik

Теперь подключим Formik, чтобы управлять состоянием данных:

import { Formik, Form } from 'formik';

// Общий компонент с Formik
const MultiStepForm = () => {
  const [currentStep, setCurrentStep] = useState<number>(0);

  // Начальные данные всех шагов
  const initialValues = {
    name: '',
    email: '',
    city: '',
    street: '',
    cardNumber: '',
    expiryDate: ''
  };

  return (
    <Formik
      initialValues={initialValues}
      onSubmit={(values) => {
        if (currentStep === steps.length - 1) {
          console.log('Форма отправлена:', values);
        } else {
          nextStep();
        }
      }}
    >
      {({ values }) => (
        <Form>
          <h1>{steps[currentStep]}</h1>
          {renderStep(currentStep)}
          <div>
            <button onClick={prevStep} disabled={currentStep === 0}>
              Назад
            </button>
            <button type="submit">
              {currentStep === steps.length - 1 ? 'Отправить' : 'Далее'}
            </button>
          </div>
        </Form>
      )}
    </Formik>
    );
};

Теперь данные сохранены в состоянии Formik, и можно их использовать.

Добавляем валидацию с Yup

Каждому шагу будет соответствовать своя схема валидации. Вот пример:

import * as Yup from 'yup';

// Схемы валидации для каждого шага
const StepOneSchema = Yup.object().shape({
  name: Yup.string().required('Имя обязательно'),
  email: Yup.string().email('Некорректный email').required('Email обязателен')
});

const StepTwoSchema = Yup.object().shape({
  city: Yup.string().required('Город обязателен'),
  street: Yup.string().required('Улица обязательна')
});

const StepThreeSchema = Yup.object().shape({
  cardNumber: Yup.string()
    .matches(/^\d+$/, 'Номер карты должен содержать только цифры')
    .required('Номер карты обязателен'),
  expiryDate: Yup.string().required('Дата окончания обязательна')
});

const validationSchema = (step: number) => {
  switch (step) {
    case 0:
      return StepOneSchema;
    case 1:
      return StepTwoSchema;
    case 2:
      return StepThreeSchema;
    default:
      return null;
  }
};

Обновим Formik, чтобы использовать validationSchema:

<Formik
  initialValues={initialValues}
  validationSchema={validationSchema(currentStep)}
  onSubmit={(values) => {
    if (currentStep === steps.length - 1) {
      console.log('Форма отправлена:', values);
    } else {
      nextStep();
    }
  }}
>

Теперь каждый шаг валидируется своей схемой.

Сохраняем состояние между шагами

Formik уже автоматически сохраняет состояние формы, так что данные с предыдущих шагов не теряются. Но если вы хотите, чтобы они рендерились, можно в дальнейшем отображать значения в консоли или полях по умолчанию.

Типичные ошибки

Очень важно помнить, что при переключении шагов нужно быть аккуратным с валидацией. Например, если вы пытаетесь использовать данные из других шагов, но не проверяете их корректность, это может привести к неожиданным ошибкам. Также не забудьте правильно настроить зависимости всех функций, чтобы избежать лишних вызовов.

Это может быть ещё одной причиной, чтобы использовать такие библиотеки, как Formik, чтобы избежать ошибок, связанных с ручным управлением состоянием.

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