1.0.2 • Published 3 years ago

dry-forms v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Dry Forms

DryForms is a form validation library for React.
DryForms is a lightweight, leaner and easy-to-use alternative to Formik library that gives you more freedom in how to validate input values in a HTML form. Though it is inspired by Formic, it takes a different, more functional approach to the process of verifying form field values.

Install

npm install -S dry-forms
yarn add dry-forms

Usage

Wrap your form into a function and pass that function into <Validate></Validate> component. Below is an example component that has a simple 'subscribe' form:

Example 1, basic validation

import React from "react"
import { Validate } from 'dry-forms'


const required = (s: string) => s.length>0
const isEmail = (s: string) => /^[\w.%+-]+@[\w-.]+\.[\w-]{2,}$/i.test(s)

const validators = {
  name: required,
  email: [required, isEmail]
}

const initials = {name: '', email: ''}

export default function Form1() {
  return <Validate validators={validators} values={initials}>
      {(values, errors, setVal, validate) => {

          const submit = () => {
              if (validate()) {
                  console.log('submit: ', values)
              }
          }

          return <form noValidate onSubmit={e => {e.preventDefault(); submit()}}>
              <input 
                  placeholder="Your name" 
                  value={values.name} 
                  onChange={e => setVal('name', e.target.value)}
                  className={errors.includes('name') ? 'error' : ''}
              />
              <input 
                  placeholder="E-mail"
                  value={values.email} 
                  onChange={e => setVal('email', e.target.value)}
                  className={errors.includes('email') ? 'error' : ''}
              />
              <input 
                  type='submit'
                  value="Submit"
              />
          </form>
      }}
  </Validate>
}

Each field in the form can have a single validator or an array of validators assigned to it in the validators array that you pass as a property to Validate component.
Validator - is just a function! In its simplest form it takes a value and returns true if the value is valid and false otherwise.
When you set a new value for the field (by calling setVal function in the onChange handler) DryForms calls every validator for that field starting from the first item in the array. If a validation function returns false, field's name is added to the errors array and rest of validators are not called.
Validate component also takes values property, that should have initial values for the form fields.

Example 2, messages

import React, { createRef } from "react"
import { Validators, required, isEmail, minLength, Validate } from 'dry-forms'


const notEmpty = required('Field is mandatory')

const validators: Validators = {
    email: [notEmpty, isEmail('You entered invalid email address')],
    password: [notEmpty, minLength(8, 'Use 8 or more characters')],
    confirm: [
        notEmpty,
        (value: string, all, submitting) => {
            if (!submitting) {
                return true
            }
            const valid = value === all.password
            return {valid, ...(!valid && {message: 'Passwords do not match'})}
        }
    ]
}

const initials = {email: '', password: '', confirm: ''}

export default function Form2() {
    const formRef = createRef<HTMLFormElement>()

    return <Validate validators={validators} values={initials} form={formRef}>
        {(values, errors, setVal, validate, messages) => {

            const submit = () => {
                if (validate()) {
                    console.log('submit: ', values)
                }
            }

            return <form ref={formRef} noValidate onSubmit={e => {e.preventDefault(); submit()}}>
                <input 
                    name="email" 
                    placeholder="E-mail"
                    value={values.email} 
                    onChange={e => setVal('email', e.target.value)}
                    className={errors.includes('email') ? 'error' : ''}
                />
                {'email' in messages && messages.email.map((m,i) => <span key={i}>{m}</span>)}
                <input 
                    type='password'
                    name="password" 
                    placeholder="Password, use 8 or more characters"
                    value={values.password} 
                    onChange={e => setVal('password', e.target.value)}
                    className={errors.includes('password') ? 'error' : ''}
                />
                {'password' in messages && messages.password.map(m => <span key={m}>{m}</span>)}
                <input 
                    type='password'
                    name="confirm" 
                    placeholder="Confirm password"
                    value={values.confirm} 
                    onChange={e => setVal('confirm', e.target.value)}
                    className={errors.includes('confirm') ? 'error' : ''}
                />
                {'confirm' in messages && messages.confirm.map(m => <span key={m}>{m}</span>)}
                <input 
                    type='submit'
                    value="Submit"
                />
            </form>
        }}
    </Validate>
}

API Reference

Validate component

<Validate validators=...  values=... form=...>
</Validate>

validators: Record<string, Validator|Validator[]>

  type Validator = (value: any, all: Record<string, any>, sibmitting: boolean) => boolean|ValidationResult
  interface ValidationResult {
    valid: boolean
    value?: any
    message?: string
  }
  • value - a new value to validate
  • all - read-only collection of all values
  • sibmitting - whether it was called in the process of 'final' validation (e.g. in onSubmit handler), validate() sets this to true

values: Record<string, any>

initial fields' values

form: React.RefObject< HTMLElement >

optional reference to the HTML form element. If passed, first input element with invalid value will receive focus after calling validate()

Validate child/render function

<Validate >
{
  (values, errors, setVal, validate) => { /* ... */ }
}
</Validate>

values: Record<string, any>

Current field values.

errors: string[]

Array of failed field names.

setVal: (name: string, value: any, validate?: boolean = true, submitting?: boolean = false, focus?: boolean = false) => boolean

Function that sets the value for a field and optionally runs validation.

  • name - field's name
  • value - new field value
  • validate - true to set a new value and validate it or false to update a value without validation
  • submitting - if it was called in validate() presumably during submitting process
  • focus - true to try to set focus on input element

validate: (dryRun?: boolean = false) => boolean

Validate all fields, returns true if all values were sucessfully validated. The best place to call it in your onSubmit handler, e.g.

onSubmit() {
  if (validate()) {
     // values are valid, it is OK to call fetch(...) etc.
  }
}
  • dryRun - do validation in a stealthy way, i.e. without adding errors and messages
...
<input 
  type='submit'
  value="Submit"
  disabled={!validate(true)} // <- check if valid but don't tell user about errors
/>