@capdilla/react-d-form
Version:
React, React Native form builder
510 lines (434 loc) • 11.6 kB
Markdown
build easy and powerfull forms with this library
- ReactJs and React Native support
- Strong Validation
- Write your Form Componets once use any times
- Easy to handle state
- is not needed Yup, Superstruct, Joi
- [Installation](
- [ Basic Use](
- [Create Custom Form](
- [`1st Step` create a new component](
- [`2nd Step` create yours custom components](
- [`3rd Step (optional)` create form.d.ts](<#3rd-step-(optional)-create-form.d.ts>)
- [`4th Step` use your custom Form component](#4th-Step-use-your-custom-Form-component)
- [Add Typescript Support](#Add-Typescript-Support)
- [`1st Step` Add Generic type to Form Component](#1st-Step-Add-Generic-type-to-Form-Component)
- [`2nd Step` add typescript support to FormComponents](#2nd-Step-add-typescript-support-to-FormComponents)
- [Validate Form](#Validate-Form)
- [Required field](#required-field)
- [Custom error message](#Custom-error-message)
- [Validate by regex](#Validate-by-regex)
- [Validate by custom validation](#Validate-by-custom-validation)
- [Handle state and values](#Handle-state-and-values)
- [Get Async values](#Get-Async-values)
# How To use
## Installation
```bash
npm i @capdilla/react-d-form
```
## Basic Use
```jsx
import { ReactDForm } from "@capdilla/react-d-form";
const App = () => (
<ReactDForm
defaultState={{ name: "John" }}
onFormChange={formValues => {
if (formValues.validation.ISFORMVALID) {
console.log("The form is valid");
} else {
console.log("Form is not valid");
}
console.log(formValues.data.email);
console.log(formValues.data.name);
console.log(formValues.data.surname);
}}
fields={[
{
fields: [
{
name: "name",
type: "Input",
validation: { required: true }
},
{
name: "surname",
type: "Input",
validation: { required: true }
}
]
},
{
fields: [
{
name: "email",
type: "Input",
props: { type: "email" }
}
]
}
]}
/>
);
```
this example will create a form with two inputs in a row and one input in a second row, the input with the name `email` will going to be a `<input type=email/>`
this example use reactstrap but you can use any css library
```jsx
import React from "react";
import Core, { GetComponent } from "@capdilla/react-d-form";
import { Row } from "reactstrap";
import FormComponents from "../FormComponents";
const Elm = props => GetComponent(FormComponents, props);
export default class Form extends Core {
render() {
return (
<div className="av-tooltip tooltip-label-right">
<Row>
{this.rows(rows => (
<>
{this.fieldFn(rows, (r, key) => (
<Elm key={key} {...r} />
))}
</>
))}
</Row>
</div>
);
}
}
```
Is mandatory to implement `this.rows` and `this.fieldFn`
this going to be the FormComponents file
```jsx
import React, { useMemo } from "react";
import { withError } from "@capdilla/react-d-form";
import { FormGroup, Label, Col } from "reactstrap";
import Select from "react-select";
const Components = {
Input: withError(
({ name, label, onChange, onBlur, props, value, placeholder, error }) => {
return (
<Col>
<FormGroup className="form-group has-float-label">
<Label>{label}</Label>
<input
className="form-control"
value={value}
placeholder={placeholder}
name={name}
onChange={e => onChange(e.target.value)}
onBlur={e => onBlur(e.target.value)}
{...props}
/>
{error && (
<div className="invalid-feedback d-block">{error.content}</div>
)}
</FormGroup>
</Col>
);
}
),
Select: withError(
({
name,
label,
onChange,
onBlur,
props,
value,
placeholder,
error,
options
}) => {
const indexedValues = useMemo(
() =>
options.reduce(
(acc, option) => ({ ...acc, [option.value]: option }),
{}
),
[]
);
return (
<Col>
<FormGroup className="form-group has-float-label">
<Label>{label}</Label>
<Select
className={`react-select `}
classNamePrefix="react-select"
options={options}
onChange={e => onChange(e.value)}
value={indexedValues[value]}
/>
{error && (
<div className="invalid-feedback d-block">{error.content}</div>
)}
</FormGroup>
</Col>
);
}
)
};
export default Components;
```
The name of this file should be the same name of the file created in the 1st STEP
This step is optional but it going to improve the `TypeScript` support for your Form component (1st STEP)
```ts
/// <reference types="react" />
import Core from "@capdilla/react-d-form";
export default class Form<T> extends Core<T> {
render(): JSX.Element;
}
```
```jsx
import Form from "../components/Form";
const App = () => (
<Form
fields={[
{
fields: [
{
name: "name",
type: "Input",
validation: { required: true }
}
]
}
]}
/>
);
```
this it the same
```jsx
export default class Form<T> extends Core<T> {
//render implementation here
}
```
```jsx
import { FormComponent } from "@capdilla/react-d-form";
interface Iinput extends FormComponent {
onBlur: (value: any) => any;
}
interface ISelect extends FormComponent {
options: {
label: string,
value: string
}[];
}
const Components = {
Input: withError(
({
name,
label,
onChange,
onBlur,
props,
value,
placeholder,
error
}: Iinput) => {
return (
<Col>
<FormGroup className="form-group has-float-label">
<Label>{label}</Label>
<input
className="form-control"
value={value}
placeholder={placeholder}
name={name}
onChange={e => onChange(e.target.value)}
onBlur={e => onBlur(e.target.value)}
{...props}
/>
{error && (
<div className="invalid-feedback d-block">{error.content}</div>
)}
</FormGroup>
</Col>
);
}
),
Select: withError(
({
name,
label,
onChange,
onBlur,
props,
value,
placeholder,
error,
options
}: ISelect) => {
const indexedValues = useMemo(
() =>
options.reduce(
(acc, option) => ({ ...acc, [option.value]: option }),
{}
),
[]
);
return (
<Col>
<FormGroup className="form-group has-float-label">
<Label>{label}</Label>
<Select
className={`react-select `}
classNamePrefix="react-select"
options={options}
onChange={e => onChange(e.value)}
value={indexedValues[value]}
/>
{error && (
<div className="invalid-feedback d-block">{error.content}</div>
)}
</FormGroup>
</Col>
);
}
)
};
export default Components;
```
```jsx
<Form
fields={[
{
fields: [
{
name: "name",
type: "Input",
validation: { required: true }
}
]
}
]}
/>
```
`errorMessage` is not requied by default the Form component will return `FIELD_REQUIRED`
```jsx
<Form
fields={[
{
fields: [
{
name: "name",
type: "Input",
validation: {
required: true,
errorMessage: "This field cannot be empty"
}
}
]
}
]}
/>
```
in this example will validate if the input is an email
```jsx
<Form
fields={[
{
fields: [
{
name: "email",
type: "Input",
validation: {
regexType: "email",
errorMessage: "is not an email"
}
}
]
}
]}
/>
```
others regex availables
| Regex type | description |
| ---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| `email` | validate an email |
| `phone` | validate an phone |
| `number` | validate if is a number |
| `double` | validate if is a number with decimals |
| `password` | validate password with next requisits: must contain at least eight characters, at least one number and both lower and uppercase letters and special characters |
```jsx
<Form
fields={[
{
fields: [
{
name: "email",
type: "Input",
validation: {
custom: values => ({
valid: values.email === "hello@mail.com",
errorMessage: "Email must be hello@mail.com"
})
}
}
]
}
]}
/>
```
```jsx
const TestForm = () => {
const [formData, setFormData] = useState({});
const getData = () => {
setTimeout(() => {
setFormData({ formData: { name: "Jonh", surname: "Doe", age: 20 } });
}, 2000);
};
useEffect(() => {
getData();
}, []);
return (
<Form
defaultState={formData}
onFormChange={data => {
setFormData(data);
}}
fields={[
{
fields: [
{
name: "name",
type: "Input"
},
{
name: "surname",
type: "Input"
}
]
},
{
fields: [
{
name: "age",
type: "Input"
}
]
}
]}
/>
);
};
```
For more examples see **/stories** folder