В предыдущем посте мы создали простую многоэтапную форму регистрации с помощью React и React Hook Form. Форма отлично подходит для простого рабочего процесса регистрации, когда пользователям не нужно переходить между этапами. В этом посте мы рассмотрим форму, в которой порядок шагов не фиксирован и пользователям не нужно вводить всю информацию сразу. Чтобы упростить задачу, мы будем использовать пример формы из предыдущего руководства, хотя этот рабочий процесс может быть не лучшим для регистрационной формы. Вместо этого давайте представим, что у нас есть форма оформления заказа, где пользователи заполняют информацию поэтапно и могут сохранить форму в состоянии черновика, чтобы вернуться к ней позже. Конечный результат можно протестировать на Codesandbox.

Сохранение данных формы при пошаговой навигации

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

В этом посте мы внесем несколько улучшений в форму, чтобы решить вышеуказанные проблемы:

  • Удалите проверку полей для каждого шага и вместо этого покажите их состояние в Stepper.
  • Сохраняйте введенные данные формы при изменении шага, чтобы данные не терялись при навигации (однако введенные данные не сохраняются при нажатии кнопки Previous).
  • Выделите недостающие поля на странице подтверждения, чтобы пользователь мог вернуться и заполнить их при необходимости.

Мы начнем с удаления всех проверок из шагов, вместо этого это будет сделано на последнем шаге. Далее нам нужно отправить данные формы, когда пользователь нажимает на шаг. Есть несколько способов сделать это. Что мы сделаем, так это обработаем навигацию между шагами так же, как кнопку Next — она сохранит текущие данные в общий контекст. Для этого нам нужно каким-то образом инициировать событие onClick кнопки из компонента Stepper. Именно здесь становится полезным малоизвестный хук useImperativeHandle. Короче говоря, этот хук позволяет вызывать методы цели ссылки вне ее компонента (например, из родительских компонентов).

Во-первых, мы заключаем компоненты Button, Stepper и отдельных шагов в forwardRef, чтобы можно было получать ref в качестве реквизита, например:

Во-вторых, мы настроим хук useImperativeHandle внутри Button, открывая событие Button click.

Наконец, мы создадим общий ref на уровне приложения и обратный вызов onStepChange, который будет назначен каждой ссылке onClick. Мы могли бы определить onStepChange непосредственно внутри Stepper, но тогда нам нужно было бы также обернуть его в forwardRef, чтобы иметь возможность принять buttonRef.

Кажется, что форма работает правильно, и данные сохраняются, когда мы переходим к следующему шагу. Однако есть одна проблема — вы могли заметить, что иногда, когда мы хотим сделать несколько шагов вперед, например. от Contact до Confirm форма перемещается только по одному шагу за раз. Это происходит из-за того, что у нас есть два конфликтующих вида навигации — один из NavLink Stepper, а другой — из обратного вызова формы onSubmit. Чтобы исправить это, мы настроим компонент Form, чтобы он имел собственный обратный вызов onSubmit и обрабатывал переход к следующему шагу. Таким образом, к тому времени, когда навигация запускается из Form, пошаговая навигация уже выполняется, а навигация формы отбрасывается.

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

Вы можете заметить, что помимо удаления проверки поля, мы также удалили поле подтверждения пароля, чтобы упростить форму. Эта проверка теперь перенесена на последний шаг — Confirm. Чтобы упростить рендеринг и проверку, мы будем хранить все данные формы для рендеринга в виде массива объектов полей, разделенных на разделы.

Следует отметить, что, хотя это выглядит чище, чем определение всех разделов и элементов по отдельности в JSX, управление может стать сложным при изменении требований, например. если добавлена ​​дополнительная логика рендеринга.

Новым дополнением к массиву элементов является поле required, которое мы будем использовать для отключения отправки формы, если какое-либо из обязательных полей отсутствует, и для выделения обязательных полей.

Чтобы достичь первого, мы перебираем все элементы и смотрим, не пусто ли какое-либо из обязательных полей.

После этого мы можем передать это значение кнопке Submit формы, чтобы управлять ее состоянием disabled.

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

В итоге получаем приятную подсветку обязательных полей:

Отображение состояния шага в Stepper

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

Во-первых, мы начнем отслеживать посещенные шаги в контексте. Затем мы можем использовать эти данные для отображения соответствующего индикатора состояния. Чтобы сделать код менее загроможденным, давайте создадим вспомогательный компонент для рендеринга состояния шага.

Здесь нет ничего особенного, мы просто визуализируем значок состояния на основе значения логического реквизита. Чтобы отслеживать посещенные шаги, мы можем использовать хуки из React Router и сохранить путь из текущего местоположения в качестве посещенного шага.

Здесь мы используем функциональную форму setState, чтобы не объявлять ее одной из useEffect зависимостей (что может привести к поломке приложения из-за бесконечного цикла рендеринга). Нам все равно, уникальны ли посещенные шаги, мы могли бы проверить, добавлен ли уже шаг, прежде чем добавлять новый, но это выглядит как незначительная оптимизация.

Наконец, мы можем добавить в Stepper индикаторы состояния шага. Для упрощения рендеринга давайте соберем данные для навигационных ссылок в массив объектов и будем рендерить их в цикле.

Шаги Education и About будут иметь состояние успеха, если они были посещены, поскольку у них нет обязательных полей. Должно быть довольно легко добавить проверку для этих шагов, если это необходимо, или иным образом расширить проверку (например, подтвердить адрес электронной почты или пароль).

Заключение

Теперь, когда подсветка состояния шага работает, у нас есть функциональная многошаговая форма, которая может иметь широкий спектр вариантов использования. В качестве будущего улучшения мы могли бы добавить функцию «Сохранить как черновик», которая сохранит введенные неполные данные в локальное хранилище (или базу данных) и позволит пользователям вернуться к ним позже.

Первоначально опубликовано на https://claritydev.net.