react-easy-form-hook
TypeScript icon, indicating that this package has built-in type declarations

0.1.2 • Public • Published

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

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 {}
  • 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",
        }
  • 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 function getObject: () => TObject; from IForm interface returned by useForm hook.
  • formName

    • name of the form that will be added to every field as a field attribute form

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>
  • 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

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

  • 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.
  • labelAttributes

    • attributes passed to <label> tag. Consists of following attributes
    id?: string
    className?: string,
    htmlFor?: string,
  • 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;

Package Sidebar

Install

npm i react-easy-form-hook

Weekly Downloads

0

Version

0.1.2

License

MIT

Unpacked Size

82.9 kB

Total Files

75

Last publish

Collaborators

  • bartoszsz