Создание формы с несколькими независимыми шагами с формой реакции на крючок
Недавно мне пришлось создать своего рода многошаговую форму, где каждый шаг независим друг от друга. Обычно многошаговая форма отправляется только один раз после того, как все шаги выполнены и действительны. В моем случае мне нужно было, чтобы каждый шаг имел свою собственную проверку и отправку. Я знаю, это странный сценарий, но он имеет смысл, когда вам нужно каждый шаг отправлять и сохранять значения в базе данных, прежде чем переходить к следующему шагу.
Я решил поделиться тем, как мне удалось решить эту проблему, потому что я видел людей с похожими проблемами и подумал, что это может быть полезно.
Основными технологиями, которые я использовал для этого, были React и Typescript. Сначала я думал о попытке создать свой собственный компонент формы, но очень скоро понял, что буду просто заново изобретать колесо. Я нашел эту библиотеку под названием React hook form, которая показалась мне очень гибкой и простой в использовании.
Форма React hook действительно хороша и интегрируется со многими валидаторами схемы. Я решил использовать Zod, который представляет собой валидатор схемы, который позволяет создавать интерфейсы путем вывода типов, что довольно приятно.
Не буду вдаваться в подробности реализации, потому что все это есть в этом репозитории, который я создал на примере. Загляните туда, если вам это интересно.
Главное дело в том, что нам нужно создать контекст с помощью React Context API и присвоить ему экземпляр каждой формы. Просто как тот. Каждый дочерний компонент внутри поставщика контекста будет иметь доступ ко всем формам. Теперь мы можем создавать каждую форму независимо, но она по-прежнему может получать доступ к данным из других форм.
Сценарий:
Давайте создадим пользовательскую форму, где первый шаг будет содержать сведения о пользователе, а следующий — сведения об его образовании.
Шаг 1 — Данные пользователя
Шаг 2 — Обучение пользователей
Мы можем создать контекст, содержащий два экземпляра UseFormReturn, каждый со своим соответствующим типом:
export type MultiStepForm = { detailsForm: UseFormReturn<IUserDetails>, educationForm: UseFormReturn<IUserEducation>, }; export const MultiStepFormContext = React.createContext<MultiStepForm | null>(null);
Затем мы можем создать экземпляр формы для каждого интерфейса и назначить их поставщику контекста:
const UserFormPage = () => { const detailsForm = useForm<IUserDetails>({ resolver: zodResolver(userDetailsValidator), mode: 'all' }); const educationForm = useForm<IUserEducation>({ resolver: zodResolver(userEducationValidator), mode: 'all' }); return ( <MultiStepFormContext.Provider value={{ detailsForm, educationForm, }} > {/* Any child that goes here can access the forms */} </MultiStepFormContext.Provider> ); };
Наконец мы можем приступить к созданию форм. Мы можем использовать ловушку useMultiStepFormContext для возврата экземпляров форм и доступа к любым данным, которые нам нужны. Вот простой пример того, что мы можем сделать:
const UserDetailsForm = () => { const { educationForm, detailsForm } = useMultiStepFormContext(); const watchDetailsForm = useWatch(detailsForm); const { formState: { errors } } = detailsForm; return <FormProvider {...detailsForm}> <div> <div className='card-body'> <form> {/* Write the form */} </form> </div> <div> <button> Save </button> </div> </div> </FormProvider> };
Вот и все. Теперь мы можем делать все что угодно с данными в формах.
Я надеюсь, что это полезно для кого-то.