Динамическая форма реакции с Formik

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

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

Есть несколько действительно хороших библиотек, которые пытаются упростить работу с формами, например Redux-form. Хотя эта библиотека, безусловно, имеет свое применение, для простых приложений это было бы излишним, и запуск каждого изменения формы через хранилище redux может действительно повлиять на производительность при масштабировании вашего приложения.

Есть одна библиотека форм React, которая быстро становится стандартом для форм React, и одна, у которой есть светлое будущее в экосистеме React. Фактически, это рекомендуется на странице форм React прямо внизу страницы и называется Formik.

Formik - это набор компонентов React, в котором используется концепция метода рендеринга React. Девиз Formik: «Реагируйте на формы без слез!» И я уверен, что после знакомства с Formik у вас не будет слез, когда дело доходит до форм в React!

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

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

МиниФормик

Давайте теперь создадим мини-версию Formik, чтобы мы могли лучше понять, как он работает под капотом. Джаред проделал большую работу, объяснив, как была создана первая версия Formik, в своем выступлении в Аликанте, Испания, и это объяснение основано на этом выступлении на React.

При создании форм в React мы обычно используем несколько вещей, состояние React, функцию handleChange и функцию handleSubmit, которая вызывается, когда мы хотим отправить форму. Мы возьмем официальный пример формы документации React для обработки нескольких входных данных (https://reactjs.org/docs/forms.html), в этом примере используется компонент резервирования:

Это простой компонент формы, который использует синтаксис вычисляемого значения свойства для обновления состояния, поэтому вместо нескольких обработчиков изменений мы можем использовать только один обработчик и позволить ему выбирать, что делать, на основе значения event.target.value.

В приведенном выше примере резервирования у нас есть два входа с именами isGoing и numberOfGuests. Затем в состоянии мы инициализируем те же значения isGoing: true, numberOfGuests: 2.

Итак, когда вызывается handleChange, мы извлекаем часть имени объекта события и устанавливаем ключ как имя и значение из объекта события. Это удобно и позволяет нам использовать только одну функцию для обработки нескольких событий
изменения.

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

Давайте теперь абстрагируем состояние и функцию handleChange в отдельный компонент, а затем представим состояние и изменение дескриптора компоненту резервирования через props. Для этого мы будем использовать шаблон рендеринга реквизита. Если вы не знакомы с этой концепцией, я настоятельно рекомендую вам прочитать руководство Майкла Джексона по использованию рендеринга здесь.

Теперь извлечем состояние и функцию handleChange из компонента резервирования и создадим новый компонент под названием
MiniFormik:

Мы просто извлекли состояние и функцию handleChange из компонента резервирования. Вы заметите, что мы используем функцию theevent.persist(), и это позволяет нам нацеливаться на правильные данные события, поскольку setState является асинхронным.

В методе рендеринга мы возвращаем переданных потомков и вызываем их с состоянием и функцией обратного вызова handleChange. Давайте теперь реорганизуем компонент резервирования, чтобы использовать наш компонент рендеринга MiniFormik:

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

Теперь мы можем инициализировать форму нашими собственными значениями, передав initialValues prop, и мы также можем передать нашу собственную функцию onSubmit, которая будет вызываться со значениями формы после нажатия кнопки отправки. Давайте посмотрим, как это работает с нашим компонентом резервирования:

Теперь у нас нет жестко запрограммированных значений состояния внутри компонента MiniFormik, мы просто сами передаем начальные значения, затем извлекаем объект values ​​из реквизита и обновляем значения для ввода, добавляя перед ним values. Мы также передаем свойство onSubmit, которое будет вызываться после отправки формы.

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

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

Чтобы добавить функцию handleBlur, мы просто реорганизовали нашу функцию handleChange, чтобы обновлять состояние всякий раз, когда пользователь касается любого из полей. Таким образом, он обновляет затронутый объект полем, которое посещает пользователь, добавляя это имя поля в качестве ключа в объект и устанавливая для него значение true, чтобы указать, что это поле было затронуто пользователем.

Затем мы передаем функцию handleBlur дочернему компоненту в рендере. Это позволяет нам использовать функцию handleBlur внутри нашего компонента Reservation, извлекая ее из объекта props:

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

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

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

Formik

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

Formik предоставляет множество инструментов и опций для рендеринга формы. При использовании компонента Formik render prop вы можете визуализировать форму с его помощью тремя различными способами:

  1. Передача компонента в опору компонента Formik
  2. Использование метода рендеринга и передача разметки ваших форм в JSX
  3. Оберните форму с помощью Formik и добавьте ее как JSX

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

Мы рассмотрим три способа более подробно, начиная с первого метода: передача компонента в Formik через его свойство component. Мы воспользуемся примером компонента, приведенным в документации:

Компонент простой формы с одним полем ввода, он получает значения, ошибки, функции отправки, размытия и изменения через свои реквизиты, которые должны быть переданы вниз. Затем мы просто передадим компонент ContacForm в качестве значения в свойство компонента на Formik, и он внедрит необходимые свойства:

<Formik component={ContactForm} />;

Использование метода рендеринга

Второй способ рендеринга формы с Formik - это использование шаблона рендеринга, который аналогичен предыдущему методу, но позволяет вам использовать опору рендеринга Formik и передавать ему необработанную разметку JSX вашей формы или компонент, который вы импортировали:

Рендеринг как дети

Наконец, вы можете визуализировать свою форму как дочерние элементы компонента Formik, это похоже на нашу реализацию MiniFormik, которую мы создали ранее:

Примечание: вы также можете использовать свойство children и передать ему необработанную разметку вашей формы:

<Formik children={props => <ContactForm {...props} />} />

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

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

Пример использования Formik - динамическая форма с проверкой

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

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

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

Вот ссылка на завершенный репозиторий DynamicForm на github, который мы будем создавать: DynamicForm

У Formik есть несколько готовых опций, которые удовлетворяют различным требованиям, мы будем использовать Formik для создания нашей собственной динамической формы. Я использовал инструмент create-react-app для начала, нам, очевидно, понадобится formik, и мы также собираемся установить yup, который мы будем использовать для проверки входных данных:

yarn add formik yup

Это добавит необходимые нам зависимости. Сначала мы удалим часть стандартного кода приложения create response, а затем создадим компонент DynamicForm. Внутри этого компонента мы создадим простой ввод, используя Formik для начала:

Вот что отображается на странице:

У нас есть одно поле ввода с кнопкой отправки, в файле App.css есть несколько стилей css, чтобы придать форме простой стиль.

Давайте быстро рассмотрим код внутри компонента DynamicForm, чтобы понять, что именно происходит. Сначала мы используем компонент Formik от Formik и используем его функцию рендеринга для создания единого ввода. Мы передали опору onSubmit функцию, которая будет вызываться со значениями формы после того, как все они пройдут проверку.

Эта функция просто выводит значения на консоль - обычно вы передаете функцию, которая отправляет эти данные куда-нибудь на сервер. Обратите внимание, что здесь мы также используем опору validationSchema, она не является обязательной и встроена в Formik.

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

У нас также есть опора initialValues, здесь мы должны инициализировать начальные значения формы, даже если форма пуста, иначе React выдаст ошибку. Свойство initialValues должно быть объектом, ключ которого будет использоваться как имя, а значение будет начальным значением ввода. В нашем примере с одним входом мы передали:

{ firstName: 'Abdi' }

Это было получено с помощью нашей функции getInitialValues, которая извлекает значение имени и использует его в качестве ключа, а затем присваивает значение из объекта, который передается как часть массива объектов в свойствах полей. У нас также есть другой вспомогательный метод renderFeilds, этот метод просто отображает данные поля и возвращает Field компонент.

Есть несколько способов рендеринга с помощью компонента Field, и здесь мы передаем разметку, которую хотим визуализировать, используя свойство рендеринга Fields. Свойство рендеринга просто возвращает некоторый jsx, который мы ему передаем.

Здесь мы также получаем доступ к значению, имени, функциям onChange и OnBlur этого конкретного поля с помощью клавиши field. Как вы можете видеть, мы деструктурируем это из параметра props и передаем его на вход, который мы возвращаем из компонента Field. Итак, это:

<input 
{...field} 
type='text' 
/>

Эквивалентно:

<input 
name={field.name} 
value={field.value} 
onChange={field.Onchange} 
onBlur={field.onBlur} 
type='text' 
/>

Первый способ просто уплотняет наш код и делает его более лаконичным. Внутри метода renderFields мы также используем свойство label из переданного нами объекта fields.

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

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

Здесь важно понимать, что имя каждой схемы должно соответствовать
имени каждого значения, которое мы инициализируем в компоненте Formik.

Это означает, что имена, которые мы используем в initialValues, должны совпадать с именами, которые мы объявляем в файле проверки. Итак, в нашем случае здесь у нас есть одно начальное значение с именем firstName, а внутри нашего файла проверки у нас также есть firstName ключ схемы Yup.

Это очень важно, иначе Formik не будет знать, какое правило проверки применять к какому входному значению.

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

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

Мы также указываем в качестве входных данных максимум 35 символов. Итак, давайте вернемся к компоненту Formik:

Строка <form onSubmit={form.handleSubmit}> вызывает нашу функцию onSubmit, которую мы предоставили в качестве опоры для onSubmit, наша функция вызывается только после того, как проверка была выполнена, и все значения полей прошли эту проверку.

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

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

Это поведение по умолчанию, и мы можем изменить его, используя свойство validateOnChange, и установить для него значение false, если мы не хотим проверять изменения.

Мы можем добавить столько полей ввода, сколько нам нужно, давайте добавим еще несколько полей в наш компонент, просто добавив больше объектов полей в массив полей, который мы передаем в DynamicForm в качестве опоры:

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

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

Также стоит отметить, что, поскольку мы используем проверку Yup, нам также необходимо добавить правило проверки для каждого нового дополнительного поля, которое мы добавляем в нашу форму внутри нашего файла проверки:

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

Если мы не удалим это поле из файла проверки, а только удалим его из объекта полей, и это поле является обязательным, Formik не отправит форму, так как значение для этого удаленного поля не передается в файл проверки. . Вот почему нам нужно убедиться, что ключи файла проверки не только совпадают по именам значений, но также убедитесь, что значения совпадают в Formik и схеме Yup.

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

Теперь, если мы сохраним это изменение и просмотрим браузер, вы увидите, что поле города стоит перед вводом адреса:

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

Добавьте больше типов ввода в нашу динамическую форму

В нашем renderFields методе мы можем проверить type ключ каждого поля формы, которое передается как опора. В настоящее время все поля нашей формы - это просто текстовые поля.

Давайте добавим проверку, чтобы увидеть, является ли тип выбранным, и если это так, мы хотим отобразить раскрывающийся элемент. Мы делегируем это другому методу класса и назовем это renderSelect:

Метод renderSelect примет входной объект и вернет элемент выбора. Для этого мы использовали свойство рендеринга компонента Field и, как и раньше, получаем доступ к объекту поля через реквизиты, которые передаются в качестве параметра функции рендеринга.

В функции render prop мы создаем параметр по умолчанию со значением «Пожалуйста, выберите», и мы используем его как параметр по умолчанию. Обратите внимание, мы ожидаем, что ключ данных будет доступен для входного объекта, который передается в renderSelect, этот ключ данных будет массивом, содержащим все параметры, которые мы хотим отобразить.

Мы сопоставляем этот массив данных, чтобы вернуть массив элементов jsx <option>. Затем мы объединяем параметр по умолчанию с остальными параметрами в другой массив и используем его в качестве параметров выбора.

Теперь нам нужно добавить проверку внутри метода renderFields, чтобы проверить наличие поля ввода выбора в поле
prop, которое передается в:

Теперь все, что осталось сделать, это добавить данные поля выбора в нашу опору полей как объект:

Обратите внимание, что у нас есть дополнительная клавиша для выбора типа ввода; data, это список параметров, которые мы хотим использовать для раскрывающегося списка. Что касается начального значения, мы устанавливаем его на Please Select, чтобы он был выбран по умолчанию.

Затем нам нужно добавить это поле в файл проверки. Мы сделаем это поле обязательным и воспользуемся тестовой функцией, чтобы убедиться, что значение по умолчанию не выбрано. Итак, наш обновленный файл проверки должен выглядеть так:

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

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

Показать ошибки проверки

Formik предоставляет множество опор, и одними из самых полезных являются errors, touched и isValid. Внутри наших методов рендеринга мы получаем доступ к так называемому formikBag. Это объект всех значений формы, затронутых, ошибок и целого ряда свойств.

Чтобы показать ошибки проверки пользователя, мы будем использовать ошибки и затронутые реквизиты. Мы также можем использовать опору isaValid для включения и отключения кнопки отправки, но нам будет достаточно выделения полей с недопустимым вводом.

Ошибки и затронутые реквизиты представляют собой объект с именем каждого поля в качестве ключа. При возникновении ошибки объект ошибок будет содержать имя поля в качестве ключа, аналогично затронутый объект также будет иметь имена в качестве ключей, чтобы показать, что это конкретное поле было посещено пользователем.

Мы извлечем ошибки и затронутый объект из параметра props, который передается в опору рендеринга, и будем использовать их для условного применения идентификатора к каждому полю формы:

Мы проверяем, есть ли ошибки и затронутый объект, и, если они оба присутствуют для каждого поля ввода, мы возвращаем строку «hasError» в качестве значения идентификатора. Затем мы добавляем это как значение идентификатора к элементу ввода.

Значения ошибок и затронутых объектов обновляются для каждого события onChange, поэтому это означает, что всякий раз, когда происходит прикосновение к полю и в этом поле возникает ошибка, значение идентификатора будет содержать hasError и применит css проверки ошибки к этому полю. . Вот обновленный метод renderSelect:

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

Итак, внутри метода рендеринга компонента DynamicForm мы можем добавить этот простой баннер с ошибкой, который будет отображаться всякий раз, когда в форме есть хотя бы
одна ошибка:

В функции render prop мы также получаем доступ к formikBag это все свойства, которые доступны для формы, включая ошибки и затронутый объект.

Здесь мы проверяем наличие хотя бы одной ошибки, и если это так, мы назначаем класс error переменной errorMessageShow, в противном случае мы устанавливаем имя класса как «скрытый».

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

Это приведет к тому, что в качестве значения класса будет назначен «скрытый», а баннер ошибки будет скрыт от просмотра.

Мы можем дополнительно расширить форму, добавив больше типов полей формы, таких как текст и флажки. Для этого мы можем добавить еще два метода,
, и вы можете догадаться, как мы их назовем! renderCheckBox и renderTextArea:

Затем внутри функции карты метода renderFields мы проверим соответствующий ключ типа и вызовем каждый метод:

Как и раньше, нам нужно будет обновить опору fields, если мы хотим добавить флажок или текстовое поле, а также добавить необходимую проверку для каждого поля соответственно:

Теперь у нас есть обновленная форма с множеством полей для ввода и проверки:

Заключение

Formik действительно меняет правила игры, и самое замечательное в нем то, что вам не нужно изучать новый или сложный API, чтобы использовать его, поскольку он написан на чистом React. Самое замечательное в этом то, что React постоянно совершенствуется, как и ваши формы React, и вы можете воспользоваться преимуществами новой функциональности React
, не беспокоясь о том, что что-то сломается.

Первоначально опубликовано на http://fullstackstudent.com/dynamic-react-form-with-formik/ .