ts-react-form-validator

React hook wrapper for ts-form-validator package to make it handy to valide with react and hooks.

Usage no npm install needed!

<script type="module">
  import tsReactFormValidator from 'https://cdn.skypack.dev/ts-react-form-validator';
</script>

README

ts-react-form-validator

ts-react-form-validator simplifies form validation logic on projects using React with hooks. The goal of this project is to streamline form validation logic and also help developer to remember to take care of the basic stuff needed to do on every form and its field. The API is a bit more simplistic compared to original ts-form-validator -library, eventhough it is still being used internally by this library.

Installation

yarn add ts-react-form-validator

Example

Following example demonstrates use with login form. Based on former feedback, this version tries to give the barebones of the use without any extra frameworks being used, eventhough the validator -library is still used to validate a single field value input.

import React from "react";
import useForm, { FormValidationRules, MessageType } from 'ts-react-form-validator';
import validator from 'validator'; 

const rules: FormValidationRules<LoginForm> = {
    // Form level validation to validate logical dependencies between form fields. (not required)
    validateForm: (form) => form.values.email !== form.values.password || {
      isFormValid: true,
      formMessage: {
        type: MessageType.HINT,
        message: 'You may be more safe, if your password differs from your email address.'
      },
    },
    // Each field may have a custom validator and its own configuration
    fields: {
      email: {
        required: true,
        trim: true,
        validate: value =>
          (value !== undefined && validator.isEmail(value)) || {
            type: MessageType.ERROR,
            message: 'Please give a valid email address',
          },
      },
      password: {
        required: true,
        trim: true,
        validate: value =>
          (value !== undefined && validator.isLength(value, {
            max: 16,
            min: 6,
          })) || {
            type: MessageType.ERROR,
            message: 'Password must be in 6-16 characters in length',
          },
        }
    }
  }

interface FormMessageProps {
  formMessage?: MessageField;
}

const FormMessage = ({formMessage}: FormMessageProps) => {
  if (!formMessage) {
    return null;
  }
  const { type, message } = formMessage;

  return <div style={{color: type === MessageType.ERROR ? 'red' : 'black'}}>
    {message}
  </div> ;
}

interface FieldMessageProps {
  messages?: MessageFields<LoginForm>;
  field: keyof LoginForm;
}

const FieldMessage = ({
  messages,
  field, 
}): FieldMessageProps => {
  return messages && messages[field] ? 
    <div style={{color: messages[field].type === MessageType.ERROR ? 'red' : 'black'}}>
      {messages[field].message}
    </div>
  : null;
}

interface LoginForm {
  email: string;
  password: string;
}

const App = () => {
  const { 
    isFormValid, 
    formMessage, 
    messages, 
    values: {
      email,
      password
    },
    events 
  } = useForm<LoginForm, any>(
    {
      email: '',  // <== Each field should have its own default value
      password: ''
    }, 
    rules
  );

  return <div style={{padding: 16}}>    
    <FormMessage formMessage={formMessage}/>
    <div>
      <input name="email" value={values.email} {...events}/>
      <FieldMessage messages={messages} field="email"/>        
    </div>
    <div>
      <input name="password" type="password" value={values.password} {...events}/>
      {messages?.password && 
        <div style={{color: messages.password.type === MessageType.ERROR ? 'red' : 'black'}}>
          {messages.password.message}
        </div>
      }
    </div>
    <button
      disabled={ !isFormValid }
      style={ {
        ...!isFormValid && {
        opacity: 0.6
      }}}>Sign in</button>
  </div>;
};

export default  App;

API

The form logic is based useForm -hook, which will take as an input forms initial values and validation rules related to single form fields and the form as a whole.


const {
  /**
   * Current form field values as an object of type given in useForm 
   * (here FormType).
   */
  values,
  /**
   * Event handler shortcut to add eventhandlers to form element with 
   * spread operator.
   */
  events,
  /**
   * An object telling if the form field has ready filled input. This 
   * normally happens when user will leave the form field on the ui.
   */
  filled,
  /**
   * True when all form field validations have passed.
   */
  isFormValid,
  /**
   * An object listing possible messages to all form fields with errors, 
   * warnings or hints added by the validation.
   */
  messages,
  /**
   * Message indicating something that is general for the whole form. 
   * This may be added on validateForm -event handler.
   */
  formMessage,
  /**
   * A default implmentation for onBlur event handler to reduce a need to 
   * write boiler plate code.
   */
  handleBlurEvent,
  /**
   * A default implmentation for onChange event handler to reduce a need 
   * to write boiler plate code.
   */
  handleChangeEvent, 
  /**
   * Function call to change single field value with more advanced control. Use
   * this if handleChangeEvent is too restrictive, like you want to deliver
   * custom data to validator.
   */
  changeField,
  /**
   * Function call to handle field blur explicitly. This is available just in case.
   * In normal use, you should manage with `handleBlurEvent`.
   */
  blurField,
  /**
   * Initialize form with new values and set/clear filled values from all fields
   */
  resetForm,

} = useForm<FormType>({
  // initial values for form fields
}, {
  // Rules for validation. 
})

To read more about validation rules, please follow the documentation of the orginal ts-form-validation.