Easy React Headless Forms
Features
- Strongly typed
- Easily configurable
- Fast
- Supports dependent dropdowns
- Comes with built-in html field factory
- Can be used with other UI libraries
Table of contents
- Easy React Headless Forms
- Table of contents
- Examples
-
API
- useForm
- settings: FormSettings
- FieldSettings<TCustomProps>
- [DefaultFieldSettings<TCustomProps>](#defaultfieldsettings\tcustomprops)
- IForm<TObject, TCustomProps>
Examples
Simple
Let's create a simplest possible form with user data, address and start date
import React, { useMemo } from 'react';
import { FieldFactory, useForm, FieldSettings, FieldType } from 'easy-react-form-hook';
export function SimpleForm() {
const fields = useMemo(() => [
{ type: FieldType.Text, accessor: "firstName", label: "First Name", },
{ type: FieldType.Text, accessor: "lastName", label: "Last Name", },
{ type: FieldType.Text, accessor: "city", label: "City", },
{ type: FieldType.Text, accessor: "street", label: "Street", },
{ type: FieldType.Date, accessor: "startDate", label: "startDate", },
] as FieldSettings[], []);
const form = useForm({ fieldsSettings: fields });
const submit = (object: any) => {/** you api call here */}
return <>
{form.fields.map(field => <div key={field.accessor}><FieldFactory {...field} /></div>)}
<button disabled={ (!form.formState.valid && !form.formState.changed)} onClick={() => submit(form.object)}>Submit</button>
</>;
}
FieldFactory, will handle creation of all field types in following format
<label {...field.labelAttributes}>{field.label}</label>
<input {...field.attributes} />
or
<label {...field.labelAttributes}>{field.label}</label>
<select {...field.attributes}>
{field.options?.map(option => <option key={option.value} {...option} />)}
</select>
You can always implement your own factory
Nested Objects
Same as above, just change accessors and it is done.
import React, { useMemo } from 'react';
import { FieldFactory, useForm, FieldSettings, FieldType } from 'easy-react-form-hook';
export function SimpleForm() {
const fields = useMemo(() => [
{ type: FieldType.Text, accessor: "user.firstName", label: "First Name", },
{ type: FieldType.Text, accessor: "user.lastName", label: "Last Name", },
{ type: FieldType.Text, accessor: "address.city", label: "City", },
{ type: FieldType.Text, accessor: "address.street", label: "Street", },
{ type: FieldType.Date, accessor: "startDate", label: "startDate", },
] as FieldSettings[], []);
const form = useForm({ fieldsSettings: fields });
const submit = (object: any) => {/** you api call here */};
return <>
{form.fields.map(field => <div key={field.accessor}><FieldFactory {...field} /></div>)}
<button disabled={ (!form.formState.valid && !form.formState.changed)} onClick={() => submit(form.object)}>Submit</button>
</>;
}
RegExp validation
Field is validated using RegExp everytime if regex property is provided and onChange event is called./
RegExp is also provided to field as attribute pattern so it is calculated by browser as well. This allows to use css with selector such as input:valid
or input:invalid
.
Also <input type='submit'>
can laverage this validation as well.
Based on previous example, let's change input user.fistName and add regex.
{ type: FieldType.Text, accessor: "firstName", label: "First Name", regex: /[a-zA-Z]{2,15}/, },
If any of the fields in the form are not valid or not populated when required, state of the form is set to invalid form.formState.valid
= false
.
API
useForm
import { useForm } from 'east-react-from-hook'
const form = useForm(settings)
settings: FormSettings
export interface FormSettings<TObject, TCustomProps> {
fieldsSettings: FieldSettings<TCustomProps>[];
defaultSettings?: DefaultFieldSettings<TCustomProps>;
initialState?: TObject;
disabled?: boolean;
keepEmptyProperties?: boolean;
refreshObjectOnChange?: boolean;
formName?: string;
}
-
fieldSettings
- List of fields handled by form e.g.
const fields =[ { type: FieldType.Text, accessor: "name", label: "Name", }, { type: FieldType.TextArea, accessor: "comment", label: "Comment",}, ]
-
defaultSettings
- Default field settings that will be marged with all provided fields.
-
initialState
- Object from which, values will be read. If not provided, object will be initialized to empty object
{}
- Object from which, values will be read. If not provided, object will be initialized to empty object
-
disabled
- disables all form fields
-
keepEmptyProperties
- By default, fields with value = undefined, will not be included in object.
On the example of fields above. If keepEmptyProperties is false or udefined and user hasn't populated input fields, then resulting object will be{}
If user have only populated field name with value John Smith, then form object will look as below
{ name: "John Smith" }
If both fields were populated, then form object will have both properties
{ name: "John Smith", comment: "This is a new user", }
- By default, fields with value = undefined, will not be included in object.
-
refreshObjectOnChange
- If you don't need your object to be recreated on every onChange event, then you can set this property to
false
. To create a form object on demand, use functiongetObject: () => TObject;
from IForm interface returned byuseForm
hook.
- If you don't need your object to be recreated on every onChange event, then you can set this property to
-
formName
- name of the form that will be added to every field as a field attribute
form
- name of the form that will be added to every field as a field attribute
FieldSettings<TCustomProps>
Depending on the field type selected, there will be different options and field attribute to set. However, all fields have following properties in common
type: FieldType;
accessor: string;
disabled?: boolean;
label: string;
tag?:string;
-
type: FieldType
- Directly corresponds to HTML input field types. Based on selected FieldType, type of FieldSettings object will change and different properties and field attributes will be accepted.
enum FieldType { Checkbox = "checkbox", Color = "color", Date = "date", DateRange = "dateRange", Email = "email", File = "file", Month = "month", Number = "number", NumericRange = "numberRange", Range = "range", Radio = "radio", Passowrd = "password", Search = "search", Select = "select", SelectMultiple = "multiselect", Submit = "submit", Telephone = "tel", Text = "text", TextArea = "textarea", Time = "time", Week = "week", }
-
accessor
- A path to property in object, using js notation style. E.g., in object
{ user: { firstName: "John", lastName: "Smith", address: { street: "Main St." city: "New Town" } }, comment: "This is a new user", }
we have following accessors
user.firstName
user.lastName
user.address.street
user.address.city
comment
\ -
disabled
- Permanently disables a field
-
label
- label used for HTML tag
<label>
label value goes here</label>
- label used for HTML tag
-
tag
- Since this is a headless form, you might decide that you want to split this object to multiple tabs (e.g., Personal Information, Address, Account Data).
tag can be used to filter fields and use them in different parts of your app
- Since this is a headless form, you might decide that you want to split this object to multiple tabs (e.g., Personal Information, Address, Account Data).
DefaultFieldSettings<TCustomProps>
Object used to provide properties and field attributes that will be merged with all fields. If specific field settings provide the same attributes, they will override default settings.
className?: string;
classNameLabel?: string;
customProps?: TCustomProps;
type?: FieldType;
IForm<TObject, TCustomProps>
Objet returned by hook useForm
fields: FormField<TCustomProps>[];
formState: IFormState;
object: TObject;
getObject: () => TObject;
object: TObject
Object created from all the fields.
getObject: () => TObject
Function used to get final object when a `refreshObjectOnChange` is set to false.
fields: FormField[]
List of fields to be rendered. There are multiple type of fields with following attributes in common.
accessor: string;
accessors: string[];
customProps?: TCustomProps;
disabled?: boolean;
initialValue?: string | number;
labelAttributes?: FormFieldLabel;
tag?:string;
value?: any;
-
accessor
- As explained in section FieldSettings<TCustomProps>
-
accessors
- Array created by spitting accessor by dot ".". It is used internally to speed up updating form object.
-
customProps
- Object defined by user that was provided in FieldSettings
-
disabled
- Indicates if field is disabled
-
initialValue
- Value taken from object provided as
initialState
in FormSettings. It is used to decide if form field and consequently whole from has been change. See formState: IFormState changed.
- Value taken from object provided as
-
labelAttributes
- attributes passed to
<label>
tag. Consists of following attributes
id?: string className?: string, htmlFor?: string,
- attributes passed to
-
tag
- A sdescribed in section [FieldSettings<TCustomProps>]
-
value
- Current value of field. It is of a type approperiate for given FieldType
formState: IFormState
Object used to keep various information about the Form
/** Defines if all fields with validation are valid */
valid: boolean;
/** Has form changed from inital value */
changed: boolean;
/** If true, properties that are undefined will set to null, otherwise thwy will not be created in final Object */
keepEmptyProperties?: boolean;
/** Is form dissabled */
disabled?: boolean;
/** If true, final object will be recreated every time onChange event is called on any input */
refreshObjectOnChange?: boolean;