npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-simple-form-validator

v0.3.1

Published

Simple React JS library to validate form fields

Downloads

805

Readme

React simple form validator

Node.js CI Npm package version Maintenance TypeScript

React simple form validator is a simple library to validate your form fields with React JS or React native. The library is easy to use and is written with typescript.

The library exposes a useValidation hook or a ValidationComponent for class based component.

You can view a demo HERE.

1. Installation

  • Run the following commands :
# with npm
npm install react-simple-form-validator --save

# or with yarn
yarn add react-simple-form-validator

2. Use it in your app

For Functional component:

You will use useValidation hook inside your component like this :

import { useValidation } from 'react-simple-form-validator';

const MyFunction = () => {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  
  const { isFieldInError, getErrorsInField, isFormValid } = useValidation({
    fieldsRules: {
      email: { email: true },
      name: { required: true }
    },
    state: { email, name }
  });
  
  return (
    <form>
      <input
            id="email"
            type="text"
            onChange={(e) => setEmail(e.target.value)}
            value={email}
          />
      <input
            id="name"
            type="text"
            onChange={(e) => setName(e.target.value)}
            value={name}
          />
    </form>
  );
}

You need to pass the form field state to the useValidation hook in the state propery like above.

You can also pass custom messages, labels, rules, locale (check the documentation below).

The description of the object returned by the hook is :

|Function / Variable|Output|Benefits| |-------|--------|--------| |isFormValid|boolean|This variable indicates if the form is valid and if there are no errors.| |isFieldInError(fieldName: string)|boolean|This function indicates if a specific field has an error. The field name will match with your form state| |getErrorMessages(separator: string, separator = '\n')|string|This method returns the different error messages bound to your form state. The argument is optional, by default the separator is a \n. Under the hood a join method is used.| |getErrorsInField(fieldName: string)|string[]|This method returns the error messages bound to the specified field. The field name will match with your form state. It returns an empty array if no error was bound to the field.| |getErrorsForField(fieldName: string)|Errors|This method returns an Errors object containing 3 properties : fieldName: string, failedRules: string[], and messages: string[].| |getFailedRules()|{[fieldName: string]: string[]}|This methods returns the failed rules of your form state| |getFailedRulesInField(fieldName: string)|string[]|This method returns the failed rules bound to a specific field name.|

For class component:

Extend "ValidationComponent" class on a your component :

import React from 'react';
import ValidationComponent from 'react-simple-form-validator';

export default class MyForm extends ValidationComponent {
  ...
}

The ValidationComponent extends the React.Component class.

To ensure form validation you have to call the validate method in a custom component (herited from ValidationComponent)

class ClassForm extends ValidationComponent {
  constructor(props) {
    super(props);

    this.fieldRules = {
      email: { email: true },
      name: { required: true }
    };

    this.state = {
      email: '',
      name: ''
    };
  }

   render() {
    return ( 
        <form>
            <input
              id="email"
              type="email"
              onChange={(e) => this.validate({ email: e.target.value, fieldRules: this.fieldRules })}
              value={this.state.email}
              placeholder="Seize an email"
            />
            <input
              id="name"
              type="text"
              onChange={(e) => this.validate({ name: e.target.value, fieldRules: this.fieldRules })}
              value={this.state.name}
              placeholder="Seize a name"
            />
          <button disabled={!this.isFormValid}>Submit</button>
        </form>
    );
   }

The validate method takes as first argument the new state to validate. For instance if you have to validate an email, pass the email value.

The second argument is your field rules validation. You can omit this argument if you set the validation prop in your component.

Once you have extended ValidationComponent your class component can use one of the below methods :

|Method / Member|Output|Benefits| |-------|--------|--------| |this.validate(newState, fieldRules?)|void|This method ensures form validation within the object passed in argument. First argument is the new state to validate. Second argument is your field rules validation| |this.isFormValid|boolean|This member indicates if the form is valid and if there are no errors.| |this.isFieldInError(fieldName: string)|boolean|This method indicates if a specific field has an error. The field name will match with your React state| |this.getErrorMessages(separator: string, separator = '\n')|string|This method returns the different error messages bound to your React state. The argument is optional, by default the separator is a \n. Under the hood a join method is used.| |this.getErrorsInField(fieldName: string)|string[]|This method returns the error messages bound to the specified field. The field name will match with your React state. It returns an empty array if no error was bound to the field.| |this.getErrorsForField(fieldName: string)|Errors|This method returns an Errors object containing 3 properties : fieldName: string, failedRules: string[], and messages: string[].| |this.getFailedRules()|{[fieldName: string]: string[]}|This methods returns the failed rules of your form state| |this.getFailedRulesInField(fieldName: string)|string[]|This method returns the failed rules bound to a specific field name.|

Existing rules :

You will find bellow the default rules available in the library defaultRules.ts :

|Rule|Benefits| |-------|--------| |numbers|Check if a state variable is a number.| |email|Check if a state variable is an email.| |required|Check if a state variable is not empty.| |date|Check if a state variable respects the date pattern. Ex: date: 'YYYY-MM-DD'| |minlength|Check if a state variable is greater than minlength.| |maxlength|Check if a state variable is lower than maxlength.| |equalPassword|Check if a state variable is equal to another value (useful for password confirm).| |hasNumber|Check if a state variable contains a number.| |hasUpperCase|Check if a state variable contains a upper case letter.| |hasLowerCase|Check if a state variable contains a lower case letter.| |hasSpecialCharacter|Check if a state variable contains a special character.|

You can also extend these rules with your own custom rules :

  • For function component :
import { defaultRules } from 'react-simple-form-validator';

// extend default rules with my custom any rule
const customRules = { ...defaultRules, any: /^(.*)$/ };

const { isFieldInError, getErrorsInField, isFormValid } = useValidation({
    fieldRules: {
      email: { email: true },
      name: { required: true }
    },
    state: { firstName, lastName, email, civility },
    rules: customRules
  });
  • For class component :
import { defaultRules } from 'react-simple-form-validator';

// extend default rules with my custom any rule
const customRules = { ...defaultRules, any: /^(.*)$/ };

<FormTest rules={customRules} />

// Or override the custom rules into the super class child constructor

// FormTest.js
class FormTest extends ValidationComponent {
  constructor(props) {
    super({...props, rules: { ...defaultRules, any: /^(.*)$/ }});

    this.fieldRules = {
      email: { email: true },
      name: { required: true }
    };
  }
}

Existing messages :

The library also contains a defaultMessages.ts file which includes the errors label for a language locale.

  • For function component :
import { defaultMessages } from 'react-simple-form-validator';

const { isFieldInError, getErrorsInField, isFormValid } = useValidation({
  fieldRules: {
    email: { email: true },
    name: { required: true }
  },
  state: { email, name },
  messages: {
    en: {...defaultMessages.en, numbers: "error on numbers !"},
    fr: {...defaultMessages.fr,numbers: "erreur sur les nombres !"}
  }
});
  • For class component :

You can override the messages component React props :

import { defaultMessages } from 'react-simple-form-validator';

const messages = {
  en: {...defaultMessages.en, numbers: "error on numbers !"},
  fr: {...defaultMessages.fr,numbers: "erreur sur les nombres !"}
};

<FormTest messages={messages} />

Custom labels for error message interpolation :

You can add custom labels, which will be useful if you want to change the error messages label or translate it to the local language :

  • For function component :
import { defaultMessages } from 'react-simple-form-validator';

const { isFieldInError, getErrorsInField, isFormValid } = useValidation({
  fieldRules: {
    email: { email: true },
    name: { required: true }
  },
  state: { email, name },
  labels: {
    name: 'Name',
    email: 'E-mail',
    number: 'Phone number'
  }
});
  • For class component :
const labels = {
  name: 'Name',
  email: 'E-mail',
  number: 'Phone number'
};

<FormTest labels={labels} />

// Or override the custom labels into the super class child constructor

// FormTest.js
class FormTest extends ValidationComponent {
  constructor(props) {
    const labels = {
      name: 'Name',
      email: 'E-mail',
      number: 'Phone number'
    };
    super({...props, labels});

    this.fieldRules = {
      email: { email: true },
      name: { required: true }
    };
  }
}

Specify language locale :

You can specify the default custom local language :

  • For function component :
const { isFieldInError, getErrorsInField, isFormValid } = useValidation({
  fieldRules: {
    email: { email: true },
    name: { required: true }
  },
  state: { email, name },
  locale: 'fr'
});
  • For class component :
<FormTest locale="fr" />

// Or override the custom locale into the super class child constructor

// FormTest.js
class FormTest extends ValidationComponent {
  constructor(props) {
    super({...props, locale: 'fr'});

    this.fieldRules = {
      email: { email: true },
      name: { required: true }
    };
  }
}

3. Complete example

You can find a concrete Functional component example on FunctionForm.tsx (Typescript) :

Function Component:

import React, { Fragment, FunctionComponent, useState } from 'react';
import { defaultMessages, defaultRules, FieldsToValidate, useValidation } from 'react-simple-form-validator';

interface FunctionFormProps {
  validation: FieldsToValidate;
}

const FunctionForm: FunctionComponent<FunctionFormProps> = (props) => {
  const [touchedFields, setTouchedFields] = useState({
    civility: false,
    email: false,
    firstName: false,
    lastName: false
  });
  const [email, setEmail] = useState('');
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [civility, setCivility] = useState('');

  const { isFieldInError, getErrorsInField, isFormValid } = useValidation({
    fieldsRules: props.validation,
    state: { firstName, lastName, email, civility },
    rules: { ...defaultRules, customCivilityRule: /^(Mrs|Ms|Miss)$/ },
    messages: {
      ...defaultMessages,
      en: { ...defaultMessages['en'], customCivilityRule: 'Civility is incorrect (Mrs/Ms/Miss)' }
    }
  });

  const onBlurHandler = (event: React.FormEvent<HTMLElement>, field: string) =>
    setTouchedFields((prevFields) => ({ ...prevFields, [field]: true }));

  const formSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log('Form values : ', civility, email, firstName, lastName);
    setFirstName('');
    setLastName('');
    setEmail('');
    setCivility('');
    setTouchedFields({ firstName: false, lastName: false, email: false, civility: false });
  };

  return (
    <Fragment>
      <h2>Functional Form</h2>
      <form onSubmit={formSubmitHandler}>
        <div className="form-control">
          <label htmlFor="gender">Civility</label>
          <input
            id="civility"
            type="text"
            onChange={(e) => setCivility(e.target.value)}
            onBlur={(e) => onBlurHandler(e, 'civility')}
            value={civility}
            placeholder="Seize a civility (Mrs or Ms or Miss)"
          />
          <p className="error-text">
            {touchedFields.civility && isFieldInError('civility') && getErrorsInField('civility').join('\n')}
          </p>
        </div>
        <div className="form-control">
          <label htmlFor="email">Email</label>
          <input
            id="email"
            type="email"
            onChange={(e) => setEmail(e.target.value)}
            onBlur={(e) => onBlurHandler(e, 'email')}
            value={email}
            placeholder="Seize an email"
          />
          <p className="error-text">
            {touchedFields.email && isFieldInError('email') && getErrorsInField('email').join('\n')}
          </p>
        </div>
        <div className="form-control">
          <label htmlFor="firstName">First name</label>
          <input
            id="firstName"
            type="text"
            onChange={(e) => setFirstName(e.target.value)}
            onBlur={(e) => onBlurHandler(e, 'firstName')}
            value={firstName}
            placeholder="Seize a first name"
          />
          <p className="error-text">
            {touchedFields.firstName && isFieldInError('firstName') && getErrorsInField('firstName').join('\n')}
          </p>
        </div>
        <div className="form-control">
          <label htmlFor="astName">Last Name</label>
          <input
            id="lastName"
            type="text"
            onChange={(e) => setLastName(e.target.value)}
            onBlur={(e) => onBlurHandler(e, 'lastName')}
            value={lastName}
            placeholder="Seize a last name"
          />
          <p className="error-text">
            {touchedFields.lastName && isFieldInError('lastName') && getErrorsInField('lastName').join('\n')}
          </p>
        </div>
        <div className="actions">
          <button disabled={!isFormValid}>Submit</button>
        </div>
      </form>
    </Fragment>
  );
};

export default FunctionForm;

Class component:

You can find a concrete Class component example on ClassForm.tsx (Typescript) :

import ValidationComponent, { ClassValidationProps, FormState } from 'react-simple-form-validator';
import { Fragment } from 'react';

class ClassForm extends ValidationComponent<ClassValidationProps, FormState> {
  constructor(props: ClassValidationProps) {
    super(props);

    this.state = {
      civility: '',
      email: '',
      firstName: '',
      lastName: '',
      touchedFields: { civility: false, firstName: false, lastName: false, email: false }
    };
  }

  onBlurHandler(event: React.FormEvent<HTMLElement>, field: string): void {
    this.setState({ touchedFields: { ...this.state.touchedFields, [field]: true } });
  }

  formSubmitHandler(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const { civility, email, firstName, lastName } = this.state;

    console.log('Form Values : ', civility, email, firstName, lastName);

    this.setState({
      civility: '',
      email: '',
      firstName: '',
      lastName: '',
      touchedFields: { civility: false, firstName: false, lastName: false, email: false }
    });
  }

  render(): JSX.Element {
    return (
      <Fragment>
        <h2>Class based Form</h2>
        <form onSubmit={this.formSubmitHandler.bind(this)}>
          <div className="form-control">
            <label htmlFor="gender">Civility</label>
            <input
              id="civility"
              type="text"
              onChange={(e) => this.validate({ civility: e.target.value })}
              onBlur={(e) => this.onBlurHandler(e, 'civility')}
              value={this.state.civility}
              placeholder="Seize a civility (Mrs or Ms or Miss)"
            />
            <p className="error-text">
              {this.state.touchedFields.civility &&
                this.isFieldInError('civility') &&
                this.getErrorsInField('civility').join('\n')}
            </p>
          </div>
          <div className="form-control">
            <label htmlFor="email">Email</label>
            <input
              id="email"
              type="email"
              onChange={(e) => this.validate({ email: e.target.value })}
              onBlur={(e) => this.onBlurHandler(e, 'email')}
              value={this.state.email}
              placeholder="Seize an email"
            />
            <p className="error-text">
              {this.state.touchedFields.email &&
                this.isFieldInError('email') &&
                this.getErrorsInField('email').join('\n')}
            </p>
          </div>
          <div className="form-control">
            <label htmlFor="firstName">First name</label>
            <input
              id="firstName"
              type="text"
              onChange={(e) => this.validate({ firstName: e.target.value })}
              onBlur={(e) => this.onBlurHandler(e, 'firstName')}
              value={this.state.firstName}
              placeholder="Seize a first name"
            />
            <p className="error-text">
              {this.state.touchedFields.firstName &&
                this.isFieldInError('firstName') &&
                this.getErrorsInField('firstName').join('\n')}
            </p>
          </div>
          <div className="form-control">
            <label htmlFor="astName">Last Name</label>
            <input
              id="lastName"
              type="text"
              onChange={(e) => this.validate({ lastName: e.target.value })}
              onBlur={(e) => this.onBlurHandler(e, 'lastName')}
              value={this.state.lastName}
              placeholder="Seize a last name"
            />
            <p className="error-text">
              {this.state.touchedFields.lastName &&
                this.isFieldInError('lastName') &&
                this.getErrorsInField('lastName').join('\n')}
            </p>
          </div>
          <button disabled={!this.isFormValid}>Submit</button>
        </form>
      </Fragment>
    );
  }
}

export default ClassForm;

4. How to contribute

If you want to contribute to this project and make it better, your help is very welcome. Contributing is also a great way to learn more about social coding on Github, new technologies and and their ecosystems and how to make constructive, helpful bug reports, feature requests and the noblest of all contributions: a good, clean pull request.

Method

  1. Create a personal fork of the project on Github.
  2. Clone the fork on your local machine.
  3. Implement/fix your feature, comment your code.
  4. Follow the code style of this project, including indentation.
  5. Write or adapt tests as needed.
  6. Add or change the documentation as needed.
  7. Squash your commits into a single commit with git's interactive rebase. Create a new branch if necessary.
  8. Push your branch to your fork on Github, the remote origin.
  9. From your fork open a pull request in the correct branch. Target the project's master branch.

Install the project

Run this command to install the library dependencies :

npm install

Build the library :

npm run build 

Launch tests :

# single run
npm run test 

# watch mode
npm run test:watch

Run the React JS example project :

cd examples/react-js && yarn install

Start the example project :

yarn start
  • If you modify the library don't forget to re-run npm run build to apply modifications into the example project.