@altiore/form
Version:
Form helper for building powerful forms
329 lines (263 loc) • 9.92 kB
Markdown
# Altiore Form
`@altiore/form`
Производительные гибкие и легко расширяемые формы с простой в использовании валидацией и наиболее удобным API [**@altiore/form**](https://www.npmjs.com/package/@altiore/form)
<a href="https://www.npmjs.com/package/@altiore/form" target="_blank">
<img src="https://img.shields.io/npm/v/@altiore/form.svg" alt="NPM Version" />
</a>
**English version**
[**README.md**](https://github.com/altiore/form/blob/main/README.md)
## Зачем?
Давайте смотреть правде в глаза, формы в React многословны и неудобны
Эта библиотека поможет облегчить и ускорить работу с формами.
Она выполняет следующие основные задачи:
1. Валидация форм
2. Управление отправкой данных
3. Удобная кастомизация переиспользуемых компонентов формы (инпутов, селектов,...)
### Особенность:
Эта библиотека, в отличие от большинства других подобных, не хранит состояние полей ввода.
В рамках @altiore/form мы считаем, что данные, введенные в форму, хранятся на странице.
Если вам нужно обеспечивать хранение данных из инпутов - у вас есть полная свобода реализовать это, используя ваш любимый менеджер состояний.
Это особенность означает, что если вы скроете поля для ввода, данные не будут отправлены.
Другими словами: скрытие данных должно быть эквивалентно отправке и в момент скрытия данных нужно их сохранять, используя ваш любимый менеджер состояний
## Установка:
#### npm
```bash
npm i @altiore/form -S
```
#### yarn
```bash
yarn add @altiore/form
```
## Простейшее использование
```tsx
import React, {useCallback} from 'react';
import {Form} from '@altiore/form';
const MyForm = () => {
const handleSubmit = useCallback((values) => {
console.log('Переменные формы:', values);
}, []);
return (
<Form onSubmit={handleSubmit}>
<input name="name" />
<button type="submit">Отправить</button>
</Form>
);
};
```
## Кастомизируем поле ввода, добавляя в него доп. функции
**Позволяет кастомизировать внешний вид полей ввода, добавляет функционал валидации и несколько других полезных функций. [Кастомизированное поле ввода в деталях](.docs/create-field.ru.md)**
**Вы так же можете использовать [FieldArray](.docs/create-field-array.ru.md) для массивов**
```tsx
import React, {useCallback} from 'react';
import {createField, Form} from '@altiore/form';
/**
* error здесь добавляются вспомогательной функцией createField
* name и label приходят из свойств, указанных в месте использования
*/
const FieldView = ({fieldProps, inputProps, label}) => {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
<span>{fieldProps.error}</span>
</div>
);
};
export const Field = createField(FieldView);
const MyForm = () => {
const handleSubmit = useCallback((values) => {
console.log('Переменные формы', values);
}, []);
return (
<Form onSubmit={handleSubmit}>
<Field
label="Имя"
name="name"
validate={/* здесь вы можете добавить функцию/массив функций для валидации */}
/>
<button type="submit">Отправить</button>
</Form>
);
};
```
## Валидации форм
> Мы предпочитаем валидацию полей на уровне поля. По аналогии с тем,
> как это реализовано в браузере.
> Но вы можете так же валидировать все данные в момент отправки
```tsx
import React, {useCallback} from 'react';
import {Form, isEmail, isRequired} from '@altiore/form';
const tooShort = (value) => {
if (value.length < 5) {
return 'Слишком коротко';
}
};
const MyForm = () => {
const handleSubmit = useCallback((values) => {
console.log('Переменные формы:', values);
}, []);
return (
<Form onSubmit={handleSubmit}>
<Field label="E-mail" name="email" validate={[isRequired(), isEmail()]} />
<Field label="Длинное имя" name="long" validate={tooShort} />
<button type="submit">Отправить</button>
</Form>
);
};
```
#### Вы можете валидировать данные и глобально тоже:
```tsx
import React, {useCallback} from 'react';
import {Form, isEmail, isRequired} from '@altiore/form';
const validate = (values) => {
const errors = {};
if (values.long?.length < 5) {
errors.long = 'Слишком коротко';
}
return errors;
};
const MyForm = () => {
const handleSubmit = useCallback((values, setErrors) => {
const errors = validate(values);
if (Object.keys(errors)?.length) {
setErrors(errors);
return;
}
console.log('Верные данные для отправки:', values);
}, []);
return (
<Form onSubmit={handleSubmit}>
<Field label="Длинное имя" name="long" />
<button type="submit">Отправить</button>
</Form>
);
};
```
## С использованием TypeScript:
```tsx
import React, {useCallback} from 'react';
import {FieldProps, createField, Form} from '@altiore/form';
interface IField {
label: string;
}
const FieldView = ({fieldProps, inputProps, label}: FieldProps<IField>) => {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
<span>{fieldProps.error}</span>
</div>
);
};
export const Field = createField<IField>(FieldView);
interface FormState {
name: string;
}
const MyForm = () => {
const handleSubmit = useCallback((values: FormState) => {
console.log('Переменные формы:', values);
}, []);
return (
<Form<FormState> onSubmit={handleSubmit}>
<Field<FormState>
label="Имя"
name="name"
validate={/* ты можешь добавить функцию/массив функций для валидации здесь */}
/>
<button type="submit">Отправить</button>
</Form>
);
};
```
## Пример всего доступного в библиотеке функционала
```tsx
import React, {useCallback} from 'react';
import {FieldProps, createField, createFieldArray, createSubmit, Form, isRequired} from '@altiore/form';
interface IField {
label: string;
}
const FieldView = createField<IField>(({fieldProps, inputProps, label}: FieldProps<IField>) => {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
<span>{fieldProps.error}</span>
</div>
);
});
export interface IFieldArray {
label?: string;
}
export const FieldIngredients = createFieldArray<IFieldArray>(({list}) => {
const renderIng = useCallback(({key, remove}) => {
return (
<div key={key}>
<div>
<div>
<button
onClick={remove}
type="button">
-
</button>
</div>
<div>
<Field
label="Ингредиент"
{/* обрати внимание, что здесь нужно писать локальное имя переменной. Поддерживается бесконечная вложенность */}
name="title"
validate={[isRequired(null)]}
/>
<Field
label="Описание ингредиента"
{/* обрати внимание, что здесь нужно писать локальное имя переменной. Поддерживается бесконечная вложенность */}
name="desc"
/>
</div>
</div>
</div>
);
}, []);
return (
<>
{list.map(renderIng)}
<button
onClick={list.add}
type="button">
Добавить ингредиент
</button>
</>
);
});
export interface ISubmit {
children: string;
className?: string;
skipUntouched?: boolean;
}
export const Submit = createSubmit<ISubmit>(
({isInvalid, isSubmitting, isUntouched, ...props}: SubmitProps<ISubmit>) => {
return (
<button {...props} disabled={isInvalid || isSubmitting || isUntouched} />
);
},
);
interface FormState {
name: string;
}
const MyForm = () => {
const handleSubmit = useCallback((values: FormState) => {
console.log('form.values is', values);
}, []);
return (
<Form<FormState> onSubmit={handleSubmit}>
<Field<FormState>
label="Label"
name="name"
validate={isRequired(null)}
/>
<FieldIngredients label="Ingredients" name="ingredients" />
<Submit>Submit</Submit>
</Form>
);
};
```
[Детальный пример валидации](.docs/valid.md)