Оптимизация пользовательского взаимодействия: создание эффективных форм в React

Оптимизация пользовательского взаимодействия: создание эффективных форм в React

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

Основы работы с формами в React

Прежде чем погрузиться в детали оптимизации, важно понять основные принципы работы с формами в React. React предлагает два подхода к обработке форм: контролируемые и неконтролируемые компоненты.

Контролируемые компоненты

Контролируемые компоненты в React представляют собой элементы формы, значения которых контролируются React-компонентом. Это означает, что состояние элемента формы хранится в state компонента и обновляется через callback-функции.

Пример контролируемого компонента:

jsx

import React, { useState } from ‘react’;

function ControlledForm() {
const [inputValue, setInputValue] = useState(»);

const handleChange = (event) => {
setInputValue(event.target.value);
};

const handleSubmit = (event) => {
event.preventDefault();
console.log(‘Отправленное значение:’, inputValue);
};

return (



);
}

Неконтролируемые компоненты

Неконтролируемые компоненты, в свою очередь, позволяют React оставить контроль над состоянием элемента формы самому DOM. В этом случае, для получения значений полей формы используются рефы.

Пример неконтролируемого компонента:

jsx

import React, { useRef } from ‘react’;

function UncontrolledForm() {
const inputRef = useRef(null);

const handleSubmit = (event) => {
event.preventDefault();
console.log(‘Отправленное значение:’, inputRef.current.value);
};

return (



);
}

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

Валидация форм

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

Встроенная валидация HTML5

Самый простой способ добавить базовую валидацию — использовать встроенные атрибуты HTML5:

jsx





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

Кастомная валидация в React

Для более гибкой и контролируемой валидации можно реализовать собственную логику проверки:

jsx

import React, { useState } from ‘react’;

function CustomValidationForm() {
const [email, setEmail] = useState(»);
const [error, setError] = useState(»);

const validateEmail = (email) => {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
};

const handleSubmit = (event) => {
event.preventDefault();
if (!validateEmail(email)) {
setError(‘Пожалуйста, введите корректный email’);
} else {
setError(»);
console.log(‘Форма отправлена’);
}
};

return (

setEmail(e.target.value)}
/>
{error &&

{error}

}

);
}

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

Оптимизация производительности форм

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

Дебаунсинг и тротлинг

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

Пример реализации дебаунсинга:

jsx

import React, { useState, useCallback } from ‘react’;
import debounce from ‘lodash/debounce’;

function DebouncedInput() {
const [value, setValue] = useState(»);

const debouncedHandleChange = useCallback(
debounce((newValue) => {
console.log(‘Debounced value:’, newValue);
}, 300),
[]
);

const handleChange = (event) => {
const newValue = event.target.value;
setValue(newValue);
debouncedHandleChange(newValue);
};

return ;
}

Мемоизация компонентов

Использование React.memo и useMemo может значительно улучшить производительность форм, предотвращая ненужные перерендеры.

jsx

import React, { useMemo } from ‘react’;

const ExpensiveInput = React.memo(({ value, onChange }) => {
const expensiveCalculation = useMemo(() => {
// Некоторые сложные вычисления
return value.toUpperCase();
}, [value]);

return (

);
});

Работа с большими формами

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

Разделение на подкомпоненты

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

jsx

import React from ‘react’;

function PersonalInfoSection({ formData, onChange }) {
return (


);
}

function ContactInfoSection({ formData, onChange }) {
return (


);
}

function LargeForm() {
const [formData, setFormData] = React.useState({
firstName: »,
lastName: »,
email: »,
phone: »,
});

const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevData) => ({ …prevData, [name]: value }));
};

return (




);
}

Использование библиотек для управления формами

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

Пример использования Formik:

jsx

import React from ‘react’;
import { Formik, Form, Field, ErrorMessage } from ‘formik’;
import * as Yup from ‘yup’;

const validationSchema = Yup.object().shape({
email: Yup.string().email(‘Некорректный email’).required(‘Обязательное поле’),
password: Yup.string().min(6, ‘Минимум 6 символов’).required(‘Обязательное поле’),
});

function LoginForm() {
return (
{
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (








)}

);
}

Доступность форм

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

Читайте также  Техники создания адаптивного дизайна по высоте

Семантическая разметка

Использование семантически правильной разметки помогает ассистивным технологиям правильно интерпретировать структуру формы:

jsx

Персональная информация






ARIA-атрибуты

ARIA-атрибуты могут улучшить доступность форм, предоставляя дополнительную информацию для ассистивных технологий:

jsx



{errors.username && (

{errors.username}

)}

Тестирование форм

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

Модульное тестирование

Модульные тесты помогают проверить отдельные компоненты формы и их поведение:

jsx

import React from ‘react’;
import { render, fireEvent } from ‘@testing-library/react’;
import LoginForm from ‘./LoginForm’;

test(‘submits form with correct values’, () => {
const handleSubmit = jest.fn();
const { getByLabelText, getByText } = render();

fireEvent.change(getByLabelText(/email/i), { target: { value: ‘test@example.com’ } });
fireEvent.change(getByLabelText(/password/i), { target: { value: ‘password123’ } });

fireEvent.click(getByText(/submit/i));

expect(handleSubmit).toHaveBeenCalledWith({
email: ‘test@example.com’,
password: ‘password123’
});
});

Интеграционное тестирование

Интеграционные тесты проверяют взаимодействие между различными компонентами формы и их интеграцию с другими частями приложения:

jsx

import React from ‘react’;
import { render, fireEvent, waitFor } from ‘@testing-library/react’;
import App from ‘./App’;

test(‘login flow works correctly’, async () => {
const { getByLabelText, getByText, findByText } = render();

fireEvent.change(getByLabelText(/email/i), { target: { value: ‘user@example.com’ } });
fireEvent.change(getByLabelText(/password/i), { target: { value: ‘password123’ } });

fireEvent.click(getByText(/login/i));

await waitFor(() => {
expect(findByText(/welcome, user/i)).toBeTruthy();
});
});

Оптимизация пользовательского опыта

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

Прогрессивное улучшение

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

jsx

function ProgressiveForm() {
const [useEnhancedUI, setUseEnhancedUI] = useState(false);

useEffect(() => {
// Проверка поддержки продвинутых функций
if (‘IntersectionObserver’ in window && ‘requestIdleCallback’ in window) {
setUseEnhancedUI(true);
}
}, []);

return (

{useEnhancedUI ? (

) : (

)}

);
}

Мгновенная валидация

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

jsx

function InstantValidationInput({ validate }) {
const [value, setValue] = useState(»);
const [error, setError] = useState(»);

const handleChange = (event) => {
const newValue = event.target.value;
setValue(newValue);
const validationError = validate(newValue);
setError(validationError || »);
};

return (


{error &&

{error}

}

);
}

Интернационализация форм

При разработке форм для международной аудитории важно учитывать особенности различных языков и культур.

Использование библиотек для локализации

Библиотеки, такие как react-intl, помогают управлять переводами и форматированием данных в соответствии с локалью пользователя:

jsx

import React from ‘react’;
import { IntlProvider, FormattedMessage, useIntl } from ‘react-intl’;

function LocalizedForm() {
const intl = useIntl();

return (



);
}

function App() {
return (



);
}

Адаптация к различным форматам данных

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

jsx

import { parsePhoneNumberFromString } from ‘libphonenumber-js’;

function InternationalPhoneInput({ value, onChange }) {
const handleChange = (event) => {
const input = event.target.value;
const phoneNumber = parsePhoneNumberFromString(input);
if (phoneNumber) {
onChange(phoneNumber.formatInternational());
} else {
onChange(input);
}
};

return ;
}

Безопасность форм

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

Защита от CSRF-атак

Использование CSRF-токенов помогает защитить формы от атак межсайтовой подделки запросов:

jsx

function SecureForm() {
const [csrfToken, setCsrfToken] = useState(»);

useEffect(() => {
// Получение CSRF-токена с сервера
fetch(‘/api/csrf-token’)
.then(response => response.json())
.then(data => setCsrfToken(data.token));
}, []);

return (


{/* Остальные поля формы */}

);
}

Санитизация пользовательского ввода

Важно всегда проводить санитизацию пользовательского ввода для предотвращения XSS-атак и инъекций:

jsx

import DOMPurify from ‘dompurify’;

function SanitizedInput({ value, onChange }) {
const handleChange = (event) => {
const dirtyValue = event.target.value;
const cleanValue = DOMPurify.sanitize(dirtyValue);
onChange(cleanValue);
};

return ;
}

Оптимизация для мобильных устройств

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

Адаптивный дизайн

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

jsx

import styled from ‘styled-components’;

const ResponsiveForm = styled.form`
display: flex;
flex-direction: column;

@media (min-width: 768px) {
flex-direction: row;
flex-wrap: wrap;
}

input {
width: 100%;
margin-bottom: 1rem;

@media (min-width: 768px) {
width: calc(50% — 0.5rem);
margin-right: 1rem;
}
}
`;

function MobileOptimizedForm() {
return (






);
}

Оптимизация для сенсорного ввода

Увеличение размера интерактивных элементов и использование специфичных для мобильных устройств типов ввода улучшает удобство использования на сенсорных экранах:

jsx

import styled from ‘styled-components’;

const TouchFriendlyInput = styled.input`
height: 44px;
font-size: 16px;
`;

const TouchFriendlyButton = styled.button`
height: 44px;
min-width: 44px;
`;

function MobileForm() {
return (




Отправить

);
}

Визуальная обратная связь

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

Индикаторы загрузки

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

jsx

import React, { useState } from ‘react’;
import styled, { keyframes } from ‘styled-components’;

const spin = keyframes`
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
`;

Читайте также  Положительное влияние маркетплейсов на российских онлайн-продавцов

const Spinner = styled.div`
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 24px;
height: 24px;
animation: ${spin} 1s linear infinite;
`;

function SubmitButton({ isLoading, children }) {
return (

);
}

function AsyncForm() {
const [isLoading, setIsLoading] = useState(false);

const handleSubmit = async (event) => {
event.preventDefault();
setIsLoading(true);
try {
await submitFormData(event.target);
} finally {
setIsLoading(false);
}
};

return (

{/* Поля формы */}
Отправить

);
}

Анимации состояний

Плавные анимации при изменении состояний формы могут сделать взаимодействие более приятным и информативным:

jsx

import React, { useState } from ‘react’;
import styled, { css } from ‘styled-components’;

const InputWrapper = styled.div`
position: relative;
margin-bottom: 20px;
`;

const InputLabel = styled.label`
position: absolute;
top: 0;
left: 10px;
transition: all 0.3s ease;
${props => props.isFocused && css`
top: -20px;
font-size: 12px;
color: #3498db;
`}
`;

const InputField = styled.input`
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
transition: border-color 0.3s ease;
&:focus {
outline: none;
border-color: #3498db;
}
`;

function AnimatedInput({ label, type = ‘text’ }) {
const [isFocused, setIsFocused] = useState(false);
const [value, setValue] = useState(»);

const handleFocus = () => setIsFocused(true);
const handleBlur = () => setIsFocused(value !== »);
const handleChange = (e) => setValue(e.target.value);

return (

{label}


);
}

Автоматическое заполнение форм

Поддержка автозаполнения браузера может значительно ускорить процесс заполнения форм для пользователей.

Использование атрибутов автозаполнения

Правильное использование атрибутов autocomplete помогает браузерам точнее определить, какую информацию следует автоматически заполнить:

jsx

function AutofillForm() {
return (








);
}

Многошаговые формы

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

Реализация многошаговой формы

Разделение формы на несколько шагов может сделать процесс заполнения менее утомительным для пользователя:

jsx

import React, { useState } from ‘react’;

function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
name: »,
email: »,
address: »,
payment: »,
});

const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prevData => ({ …prevData, [name]: value }));
};

const nextStep = () => setStep(prevStep => prevStep + 1);
const prevStep = () => setStep(prevStep => prevStep — 1);

const renderStep = () => {
switch(step) {
case 1:
return (
<>

Шаг 1: Личная информация





);
case 2:
return (
<>

Шаг 2: Адрес





);
case 3:
return (
<>

Шаг 3: Оплата





);
default:
return null;
}
};

return (

{renderStep()}

);
}

Оптимизация для SEO

Хотя формы сами по себе не являются основным фактором SEO, правильная их реализация может косвенно влиять на поисковую оптимизацию сайта.

Семантическая разметка

Использование семантически правильных тегов и атрибутов помогает поисковым системам лучше понимать структуру и назначение форм:

jsx

function SEOFriendlyForm() {
return (




);
}

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

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

Виртуализация длинных списков

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

jsx

import React from ‘react’;
import { FixedSizeList as List } from ‘react-window’;

const countries = [/* Длинный список стран */];

function CountrySelect() {
const renderRow = ({ index, style }) => ();

return (

);
}

Ленивая загрузка компонентов формы

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

jsx

import React, { lazy, Suspense } from ‘react’;

const LazyComplexFormSection = lazy(() => import(‘./ComplexFormSection’));

function LargeForm() {
return (



Загрузка…

}>



);
}

Заключение

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

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

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

Ресурсы для дальнейшего изучения

    • Официальная документация React по работе с формами
    • Библиотека Formik для управления состоянием форм в React
    • React Hook Form — легковесная библиотека для валидации форм
    • MDN Web Docs: HTML forms guide
    • Web Accessibility Initiative (WAI) руководство по созданию доступных форм
    • Google Developers: Best Practices for Form Design
    • Nielsen Norman Group: Form Design Guidelines

    Эти ресурсы предоставляют дополнительную информацию и углубленные знания по различным аспектам создания и оптимизации форм в React и веб-разработке в целом.

    Часто задаваемые вопросы

    1. Какой подход лучше использовать: контролируемые или неконтролируемые компоненты?

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

    2. Как оптимизировать производительность форм с большим количеством полей?

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

    • Разделение формы на подкомпоненты
    • Использование мемоизации (React.memo, useMemo)
    • Применение виртуализации для длинных списков
    • Ленивая загрузка сложных компонентов формы
    • Использование дебаунсинга для обработки частых изменений

    3. Как обеспечить доступность форм для пользователей с ограниченными возможностями?

    Для обеспечения доступности форм следует:

    • Использовать семантическую разметку (label, fieldset, legend)
    • Применять ARIA-атрибуты
    • Обеспечивать навигацию с помощью клавиатуры
    • Предоставлять понятные сообщения об ошибках
    • Использовать достаточный контраст цветов
    • Тестировать формы с помощью программ чтения с экрана

    4. Какие библиотеки рекомендуется использовать для управления формами в React?

    Популярные библиотеки для управления формами в React включают:

    • Formik
    • React Hook Form
    • Redux Form (для приложений, использующих Redux)
    • react-final-form

    Выбор библиотеки зависит от конкретных требований проекта и предпочтений разработчика.

    5. Как обеспечить безопасность форм?

    Для обеспечения безопасности форм необходимо:

    • Использовать HTTPS для шифрования данных при передаче
    • Применять CSRF-токены для защиты от межсайтовой подделки запросов
    • Проводить валидацию и санитизацию данных на стороне сервера
    • Ограничивать количество попыток отправки формы для предотвращения атак перебором
    • Использовать капчу или другие механизмы защиты от ботов
    • Применять правильные заголовки безопасности (например, Content Security Policy)

    6. Как оптимизировать формы для мобильных устройств?

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

    • Использовать адаптивный дизайн
    • Увеличивать размер интерактивных элементов для удобства нажатия
    • Минимизировать количество полей
    • Использовать соответствующие типы ввода (например, type=»tel» для телефонных номеров)
    • Обеспечивать автозаполнение где это возможно
    • Группировать связанные поля
    • Использовать прогрессивное раскрытие для сложных форм

    7. Как реализовать валидацию форм на стороне клиента?

    Для реализации клиентской валидации можно использовать:

    • Встроенную валидацию HTML5 (атрибуты required, pattern, min, max и т.д.)
    • Пользовательскую JavaScript-валидацию
    • Библиотеки валидации (например, Yup, Joi)
    • Возможности валидации, предоставляемые библиотеками управления формами (Formik, React Hook Form)

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

    Практические примеры

    Пример 1: Форма регистрации с валидацией

    jsx

    import React from ‘react’;
    import { Formik, Form, Field, ErrorMessage } from ‘formik’;
    import * as Yup from ‘yup’;

    const validationSchema = Yup.object().shape({
    username: Yup.string()
    .min(3, ‘Слишком короткое имя пользователя’)
    .max(50, ‘Слишком длинное имя пользователя’)
    .required(‘Обязательное поле’),
    email: Yup.string()
    .email(‘Некорректный email’)
    .required(‘Обязательное поле’),
    password: Yup.string()
    .min(8, ‘Пароль должен содержать минимум 8 символов’)
    .matches(/[a-zA-Z]/, ‘Пароль должен содержать латинские буквы’)
    .matches(/[0-9]/, ‘Пароль должен содержать цифры’)
    .required(‘Обязательное поле’),
    confirmPassword: Yup.string()
    .oneOf([Yup.ref(‘password’), null], ‘Пароли должны совпадать’)
    .required(‘Обязательное поле’),
    });

    function RegistrationForm() {
    return (
    {
    setTimeout(() => {
    alert(JSON.stringify(values, null, 2));
    setSubmitting(false);
    }, 400);
    }}
    >
    {({ isSubmitting }) => (











    )}

    );
    }

    export default RegistrationForm;

    Пример 2: Многошаговая форма заказа

    jsx

    import React, { useState } from ‘react’;
    import { Formik, Form, Field, ErrorMessage } from ‘formik’;
    import * as Yup from ‘yup’;

    const PersonalInfoSchema = Yup.object().shape({
    name: Yup.string().required(‘Обязательное поле’),
    email: Yup.string().email(‘Некорректный email’).required(‘Обязательное поле’),
    });

    const ShippingInfoSchema = Yup.object().shape({
    address: Yup.string().required(‘Обязательное поле’),
    city: Yup.string().required(‘Обязательное поле’),
    postalCode: Yup.string().required(‘Обязательное поле’),
    });

    const PaymentInfoSchema = Yup.object().shape({
    cardNumber: Yup.string().required(‘Обязательное поле’),
    expirationDate: Yup.string().required(‘Обязательное поле’),
    cvv: Yup.string().required(‘Обязательное поле’),
    });

    function MultiStepOrderForm() {
    const [step, setStep] = useState(1);
    const [formData, setFormData] = useState({});

    const handleNextStep = (values) => {
    setFormData({ …formData, …values });
    setStep(step + 1);
    };

    const handlePrevStep = () => {
    setStep(step — 1);
    };

    const handleSubmit = (values) => {
    const finalData = { …formData, …values };
    // Отправка данных на сервер
    console.log(finalData);
    };

    return (

    {step === 1 && (

    Шаг 1: Личная информация








    )}

    {step === 2 && (

    Шаг 2: Информация о доставке











    )}

    {step === 3 && (

    Шаг 3: Информация об оплате











    )}

    );
    }

    export default MultiStepOrderForm;

    Пример 3: Форма с динамическими полями

    jsx

    import React from ‘react’;
    import { Formik, Form, Field, FieldArray, ErrorMessage } from ‘formik’;
    import * as Yup from ‘yup’;

    const validationSchema = Yup.object().shape({
    name: Yup.string().required(‘Обязательное поле’),
    email: Yup.string().email(‘Некорректный email’).required(‘Обязательное поле’),
    phones: Yup.array().of(
    Yup.string().matches(/^\+?[1-9]\d{1,14}$/, ‘Некорректный номер телефона’)
    ),
    });

    function DynamicForm() {
    return (
    {
    setTimeout(() => {
    alert(JSON.stringify(values, null, 2));
    setSubmitting(false);
    }, 400);
    }}
    >
    {({ values }) => (

Советы по созданию сайтов