0.0.7 • Published 4 months ago

react-form-validation-handler v0.0.7

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

react-form-validation-handler

react-form-validation-handler is a React library for simplifying form validation. It provides a set of utilities and hooks to handle client-side form validation with ease.

Key features include:

Declarative Validation Rules: Define validation rules in a declarative manner.

Dynamic Validation: Handle dynamic validation based on user input.

Custom Validation Functions: Support for custom validation functions.

# Installation

This package requires React 16.8.4 or later.

Use the package manager npm or yarn to install react-form-validation-handler.

$ npm install react-form-validation-handler

or

$ yarn add react-form-validation-handler

# Basic Example

/* form.js */
import { FormProvider, Form } from "react-form-validation-handler";
export const { useForm, useFormRef } = FormProvider();

/* hook.js */
import { useForm } from "./form.js";
export const useFormHook = () =>
  useForm({
    FORM_CONFIG: {
      name: { isRequired: true },
      age: { min: 18, max: 16 },
    },
  });

/* customInputField.js */
import { useFormConsumer } from "react-form-validation-handler";

export const InputField = React.memo((props) => {
  const { id, name, ...restProps } = props;
  const { inputProps } = useFormConsumer({ id });
  return (
    <div>
      <div>{name}</div>
      <input {...inputProps} {...restProps} />
      {inputProps.error && <span>{inputProps.error}</span>}
    </div>
  );
});

/* basicForm.js */
import { useEffect, useRef } from "react";
import { Form } from "react-form-validation-handler";
import { useFormHook } from "./hook.js";
import { InputField } from "./customInputField.js";
export const BasicForm = () => {
  const { formRef, formId } = useFormHook();
  return (
    <Form.Provider formRef={formRef}>
      <InputField id="name" />
      <InputField id="age" />
      <Button onClick={formRef.validateForm}>Submit</Button>
    </Form.Provider>
  );
};

# Basic usage

# Form Configuration

Note:
- Its required to configure form Provider before start using useForm hook
- Global config (one time configuration)
/* form.js */
import { FormProvider, Form } from "react-form-validation-handler";
import {
  ON_CHANGE,
  ON_BLUR,
  ON_CHANGE,
  ERROR,
  ON_CHANGE_TEXT,
} from "react-form-validation-handler/constants";
const { useForm, useFormRef } = FormProvider({
  ON_CHANGE_KEY: ON_CHANGE /* use ON_CHANGE_TEXT if you are using react-native  */,
  ON_BLUR_KEY: ON_BLUR,
  VALUE_KEY: VALUE,
  ERROR_KEY: ERROR,
});
export { useForm, useFormRef, Form };

Hooks and Components

useForm: A hook for managing form state.

useFormRef: A hook for obtaining a reference to the form.

Form: A form component for use in React components.

# Getting inputprops using hook

/* customInputField.js */
import { useFormConsumer } from "react-form-validation-handler";

const InputField = React.memo((props) => {
  const { id, name, ...restProps } = props;
  const { inputProps } = useFormConsumer({ id });
  return (
    <div>
      <div>{name}</div>
      <input {...inputProps} {...restProps} />
      {inputProps.error && <span>{inputProps.error}</span>}
    </div>
  );
});

Note: Avoiding Unnecessary Re-renders

- One significant advantage of using the context-based implementation provided by react-form-validation-handler is its ability to minimize unnecessary component re-renders on every onChange.

- By managing form state through the FormProvider, components subscribing to the form state will only re-render when relevant form data changes. This can lead to improved performance in scenarios where frequent re-renders are not desired.

# Getting inputprops using context

/* customInputField.js */
import { Form } from "react-form-validation-handler";

export const InputField = React.memo((props) => {
  const { id, name, ...restProps } = props;
  return (
    <Form.Consumer
      id={id}
      inputConfig={{
        isRequired: false,
      }}
    >
      {({ inputProps }) => (
        <div>
          <div>{name}</div>
          <input {...inputProps} {...restProps} />
          {inputProps.error && <span>{inputProps.error}</span>}
        </div>
      )}
    </Form.Consumer>
  );
});

# creating basic form hook config

- Import

/* hook.js */
import { useForm } from "./form.js";

- Form Configuration

Define your form configuration using FORM_CONFIG. Each field in the form has specific validation rules.

const FORM_CONFIG = {
  name: { isRequired: true },
  age: { min: 18, max: 16 },
  company: { isRequired: true },
};

- Initialization

Initialize the form hook using useForm and provide the FORM_CONFIG and initial state.

const initialState = {
  name: "",
}; /* optional -  default state */
export const useFormHook = () =>
  useForm({
    FORM_CONFIG,
    initialState,
  });

Usage

import { useForm } from "./form.js";
const FORM_CONFIG = {
  name: { isRequired: true },
  age: { min: 18, max: 16 },
  company: { isRequired: true },
};
export const useFormHook = () =>
  useForm({
    FORM_CONFIG,
    initialState: {
      name: "",
    },
  });

# using form hook in component

- Import

/* basicForm.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

- Component Definition

The BasicForm component utilizes the useFormHook to manage form state and the Form. Form.Provider to wrap the form elements.

const BasicForm = () => {
  const { formRef, formId } = useFormHook();
  return (
    <Form.Provider formRef={formRef}>
      <InputField id="name" />
      <InputField id="age" />
      <InputField id="company" />
      <Button onClick={formRef.validateForm}>Submit</Button>
    </Form.Provider>
  );
};

Usage

/* basicForm.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

const BasicForm = () => {
  const { formRef, formId } = useFormHook();
  return (
    <Form.Provider formRef={formRef}>
      <InputField id="name" />
      <InputField id="age" />
      <InputField id="company" />
      <Button onClick={formRef.validateForm}>Submit</Button>
    </Form.Provider>
  );
};

export BasicForm;

# creating nested form hook config

- Import

/* basicForm.js */
import { newSchema } from "react-form-validation-handler";
import { useForm } from "./form.js";

- Form Configuration

Define your nested form configuration using newSchema. In this example, we have a person schema with nested properties.

const FORM_CONFIG = {
  person: newSchema({
    name: { isRequired: true },
    age: { min: 18, max: 16 },
    company: {
      isRequired: true,
      inputProps: {
        disabled: true,
      },
    },
  }),
};

- Initialization

Initialize the form hook using useForm and provide the FORM_CONFIG with nested schema.

export const useFormHook = () =>
  useForm({
    FORM_CONFIG,
  });

Usage

/* hook.js */
import { newSchema } from "react-form-validation-handler";
import { useForm } from "./form.js";

const FORM_CONFIG = {
  person: newSchema({
    name: { isRequired: true },
    age: { min: 18, max: 16 },
    company: { isRequired: true },
  }),
};
export const useFormHook = () =>
  useForm({
    FORM_CONFIG,
  });

# nested form

/* schemaForm.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

const schemaForm = () => {
  const { formRef, formId } = useFormHook();
  console.log(formRef);
  const submit = useCallback(() => {
    console.log(formRef.validateForm());
  }, []);
  return (
    <Form.Provider formRef={formRef}>
      <Form.Provider id="person">
        <InputField id="name" />
        <InputField id="age" />
        <InputField id="company" />
      </Form.Provider>
      <Button onClick={submit}>Submit</Button>
    </Form.Provider>
  );
};
  1. This is the image from console FormRef object

  1. This is the image from console after submitting form

# creating formArray hook config

/* hook.js */
import { newFormArray } from "react-form-validation-handler";
import { useForm } from "./form.js";

const FORM_CONFIG = {
  person: newFormArray({
    name: { isRequired: true },
    age: { min: 18, max: 16 },
    comapny: { isRequired: true },
  }),
};
export const useFormHook = () =>
  useForm({
    FORM_CONFIG,
  });

# FormArray

/* schemaForm.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

const schemaForm = () => {
  const { formRef, formId } = useFormHook();
  console.log(formRef);
  const submit = useCallback(() => {
    console.log(formRef.validateForm());
  }, []);
  return (
    <Form.Provider formRef={formRef}>
      <Form.Multiple id="person">
        {({ formRef: _formRef, form, formId, count, index }, arrayProps) => (
          <Form.Provider formRef={_formRef} key={formId}>
            <InputField id="name" />
            <InputField id="age" />
            <InputField id="company" />
            <button onClick={form.append}>> append</button>
            <button onClick={form.prepend}>prepend</button>
            <button onClick={form.delete}>delete</button>
            <button onClick={form.reset}>reset</button>
            <button onClick={form.clear}>clear</button>
            <button onClick={form.move}>move</button>
            <button onClick={form.swap}>swap</button>
            <button onClick={form.insert}>insert</button>
            <button onClick={form.clone}>clone</button>
            {console.log(arrayProps)}
          </Form.Provider>
        )}
      </Form.Multiple>
      <Button onClick={submit}>Submit</Button>
    </Form.Provider>
  );
};

Explanation

  • The useFormHook is used to obtain the form reference and form ID.
  • The Form.Provider wraps the entire form and provides context for form handling.
  • The Form.Multiple component is used to handle a dynamic array of form elements.
  • The InputField component is used for each form field within the dynamic array.
  • Various form manipulation buttons (append, prepend, delete,reset,clear,move,swap, insert,clone,etc.) are provided to showcase the dynamic form functionality.
  • The submit function is a callback that triggers form validation and logs the result.

Form Array methods

# Access FormRef Object using useformRef hook

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  console.log(formRef.getValues());
  console.log(formRef.getErrors());
};

# inputProps - <Object>

PropsDefault Valuetypedescription
onChangefunctionfunctionA callback function triggered when the value of the component changes.
onBlurfunctionfunctionA callback function triggered when the component loses focus (on blur).
valueanyanyThe current value of the input
errorstringstringAn error message associated with the input

# Form config / InputConfig props - <Object>

PropsDefault ValuetypevalueDescription
isRequiredfalseBooleantrue or falseA boolean indicating whether the field is required. Throws an error if the field is empty.
optionalfalseBooleantrue or falseA boolean indicating whether validation should occur only if the field has a value.
minnullnumber4A number indicating the minimum value required.
maxnullnumber8A number indicating the maximum value required.
maxLengthnullnumber7A number indicating the maximum characters allowed.
minLengthnullnumber6A number indicating the minimum characters required.
allowOnlyNumberfalseBooleantrue or falseA boolean indicating whether only numbers are allowed.
allowValidNumberfalsenumbertrue or falseA number indicating whether the entered number is valid or not.
typenullnumber"email","number"A string indicating the type of validation ("email", "number").
trimnullBooleantrue or falseA boolean indicating whether spaces are allowed.
patternnullregex/\d+/A regular expression for custom pattern validation.
messagenullobject{min: 'mimimum 8 characters required'}An object providing custom error messages for different validation scenarios
validatornullfunction(value) => ({error: value < 5 ? 'Error' : null,value})A function for custom validation logic.
callbacknullfunction({value}) => {console.log('do something')}A function called after validation.
isValidateOnBlurtrueBooleantrue or falseA boolean indicating whether to validate the field on blur.
isValidateOnChangetrueBooleantrue or falseA boolean indicating whether to validate the field on change
vatidatetrueBooleantrue or falseA boolean indicating whether to set validation to true or false.
default''anystring or object or numberThe default value for the field

# getValues method

Get form values using getValues method

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  console.log(formRef.getValues());
};

# getErrors method

Get form errors using getErrors method

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  console.log(formRef.getErrors());
};

# clearForm method

clearForm will reset the form values to default

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.clearForm();
};

# resetForm method

resetForm will reset the form values to initialState

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.resetForm();
};

# getFormRef method

getFormRef will be used for nested form to access nested form ref

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.getFormRef(["person"]);
};

# getFormRefById method

getFormRefById similar to useFormRef for accessing formRef based on formId

/* hook.js */
const form = ({ formId }) => {
  const { formRef: nestedFormRef, formId } = formRef.getFormRefById(formId);
  console.log(nestedFormRef);
};

# renderForm method

renderForm will manually render the component

Note: By default components are not rendered on every change

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.renderForm();
};

# setFormValues method

setFormValues will be used for prefill form values

Note: By default only keys in the given object will be modified. If you want to reset and set new values, set second params to true

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const IS_RESET_FORM = true;
  formRef.setFormValues(
    {
      name: "Person",
    },
    IS_RESET_FORM
  );
};

# validateForm method

validateForm will be used to validate the form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const onSubmit = () => {
    console.log(formRef.validateForm());
  };
};

# resetValues method

validateForm will be used to reset values based on keys

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.resetValues(["name"]);
};

# clearValues method

clearValues will be used to clear values based on keys

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.clearValues(["name"]);
};

# deleteFormConfig method

deleteFormConfig will be used to delete the formConfig

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.deleteFormConfig(["age"]);
};

# modifyFormConfig method

modifyFormConfig will be used to modify the formConfig

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.modifyFormConfig({
    age: {
      type: "number",
    },
  });
};

# resetFormConfig method

resetFormConfig will be used to reset the formConfig and set the newConfig

Note: This will remove the old config and set the new config

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  formRef.modifyFormConfig({
    age: {
      type: "number",
    },
  });
};

Form Array Props

# append method

append will add the new form after the target Form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { append } = formRef.formArrayProps;

  const onAdd = () => {
    const targetFormId = __formId__;
    const values = [{ name: "steve" }];
    const noOfFormsToBeAdded = 2;
    append(targetFormId, values, noOfFormsToBeAdded);
  };
};

# prepend method

prepend will add the new form before the target Form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend } = formRef.formArrayProps;

  const onAddBefore = () => {
    const targetFormId = __formId__;
    const values = [{ name: "steve" }];
    const noOfFormsToBeAdded = 2;
    prepend(targetFormId, values, noOfFormsToBeAdded);
  };
};

# delete method

delete method will be used to delete the target form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend, delete: deleteForm } = formRef.formArrayProps;

  const onDeleteForm = () => {
    const targetFormId = [__formId1__, __formId2__];
    const values = [{ name: "steve" }];
    deleteForm(targetFormId);
  };
};

# clone method

clone method will be used to clone the target form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend, clone } = formRef.formArrayProps;

  const onCloneForm = () => {
    const sourceFormId = __formId1__;
    const noOfFormsToBeCloned = 2;
    clone(sourceFormId, noOfFormsToBeCloned);
  };
};

# clear method

clear method will be used to clear the target form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { clear } = formRef.formArrayProps;

  const onClearForm = () => {
    const formIds = [__formId1__];
    clear(formIds);
  };
};

# reset method

reset method will be used to reset the target form

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend, reset } = formRef.formArrayProps;

  const onResetForm = () => {
    const formIds = [__formId1__];
    reset(formIds);
  };
};

# move method

move method will be used to move the target form to particular index position

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend, move } = formRef.formArrayProps;

  const onMoveForm = () => {
    const currentFormId = __formId1__;
    const targetFormId = __formId2__;
    move(currentFormId, targetFormId);
  };
};

# swap method

move method will be used to swap between two forms

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend, swap } = formRef.formArrayProps;

  const onSwapForm = () => {
    const currentFormId = __formId1__;
    const targetFormId = __formId2__;
    forms(currentFormId, targetFormId);
  };
};

# insert method

insert method will be used to insert the new form on particular index

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { prepend, insert } = formRef.formArrayProps;

  const onInsertForm = () => {
    const targetFormId = __formId__;
    const values = [{ name: "steve" }];
    const noOfFormsToBeInserted = 2;
    insert(targetFormId, values, noOfFormsToBeInserted);
  };
};

# setFormRefArray method

setFormRefArray method will be used to change the order of the form and set the new form array

/* hook.js */
import { useFormRef } from "./hook.js";

const form = ({ formId }) => {
  const { formRef, formId } = useFormRef(formId);
  const { setFormRefArray } = formRef;

  const onSetFormRefArray = () => {
    setFormRefArray([formRef, formRef]);
  };
};

# Render Form

Note:

- set to true only if required. By default it will be false

- It will render component on every form change. Please use it only if required

/* hook.js */
import { newSchema } from "react-form-validation-handler";
import { useForm } from "./form.js";

const FORM_CONFIG = {
  person: newSchema({
    name: { isRequired: true },
    age: { min: 18, max: 16 },
    comapny: { isRequired: true },
  }),
};
const RENDER_FORM = true;
export const useFormHook = () =>
  useForm({
    FORM_CONFIG,
    renderForm: RENDER_FORM,
  });

# Don't reset form on unmount

Note:

- This will always maintain the form state even if you navigate from one page to another page

- If page is refreshed it will reset the form value

- It can be used for multiple step form

/* step_1.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

const Form = () => {
  const { formRef, formId } = useFormHook();
  const submit = useCallback(() => {
    if (formRef.validateForm().isValid) {
      // navigate("step_2", {
      //   formId,
      // });
    }
  }, []);
  return (
    <Form.Provider formRef={formRef} dontResetOnUnmount>
      <Form.Provider id="step_1">
        <Form.Provider id="person">
          <InputField id="name" />
          <InputField id="age" />
          <InputField id="company" />
        </Form.Provider>
      </Form.Provider>
      <Button onClick={submit}>Submit</Button>
    </Form.Provider>
  );
};
/* step_2.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

const Form = ({ formId }) => {
  const { formRef } = useFormRef(formId);
  const submit = useCallback(() => {
    console.log(formRef.validateForm());
  }, []);
  return (
    <Form.Provider formRef={formRef} dontResetOnUnmount>
      <Form.Provider id="step_2">
        <Form.Provider id="person">
          <InputField id="name" />
          <InputField id="age" />
          <InputField id="company" />
        </Form.Provider>
      </Form.Provider>
      <Button onClick={submit}>Submit</Button>
    </Form.Provider>
  );
};

# Form Controller

This will helps to controll multiple input props

/* step_1.js */
import { useEffect, useRef } from "react";
import { useFormHook, Form } from "./hook.js";
import { InputField } from "./customInputField.js";

const Form = () => {
  const { formRef, formId } = useFormHook();
  const submit = useCallback(() => {
    if (formRef.validateForm().isValid) {
      // navigate("step_2", {
      //   formId,
      // });
    }
  }, []);
  return (
    <Form.Provider formRef={formRef} dontResetOnUnmount>
      <Form.Provider id="step_1">
        <Form.Provider id="person">
          {({ values }) => (
            <>
              <InputField id="name" />
              <Form.Controller disabled={!values.name}>
                <InputField id="age" />
                <InputField id="company" />
              </Form.Controller>
            </>
          )}
        </Form.Provider>
      </Form.Provider>
      <Button onClick={submit}>Submit</Button>
    </Form.Provider>
  );
};

# Whether this package will support for react-native

Yes, this package will support for both react and react-native

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

Copyright (c) 2023-present Chrissie Fernando