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-use-validation

v0.0.8

Published

A non-intrusive validation hook for React

Downloads

6

Readme

Unlicense license NPM version

react-use-validation

Wouldn't it be great if there was a way to validate form fields without delegating control of form state to a third party library?

react-use-validation is a non-intrusive validation hook for React.

Non-intrusive?

This hook is not a form state library.

There's no need to attach refs or spread props on input components, learn complex state management APIs, or bloat JSX with validation configuration.

Quick start

  • Define some state.
  • Define a validation rule with a ruleName, some state to validate, and a validation function that takes that state and returns true (valid) or false (invalid).
  • Run validation whenever you like (or see the automatic validation example).
  • Render a hint for the user if validation fails.
  • Before submitting, run validation against all rules.
// Define some state
const [str, setStr] = React.useState('')

const validation = useValidation({
  // Define a validation rule
  // rule_name: [state_to_validate, function_that_returns_true_or_false]
  myValidationRule: [str, s => Boolean(s.length)],
})

const onSubmit = () => {
  // Before submitting, run validation against all rules
  if (validation.validate()) {
    // Submit here...
  }
}

return (
  <form onSubmit={onSubmit}>
    <input
      value={str}
      onChange={e => {
        // Validate a specific rule, passing an up-to-date state value
        validation.validate('myValidationRule', e.target.value)
        setStr(e.target.value)
      }}
    />
    {/* Render a hint if validation fails for a specific rule */}
    {validation.isInvalid('myValidationRule') && <div>{'Please enter something'}</div>}
  </form>
)

Installation

npm i react-use-validation

This package depends on react (v16.8.0 or later).

Usage

Import

Import the hook in a component file:

import useValidation from 'react-use-validation'

Define rules

Pass a rules object to the hook. Each property of this object defines a single validation rule:

const rules = {
  ruleName: [stateToValidate, validationFunction],
}

const validation = useValidation(rules)

The property key is the rule name. This key is used to run validation for a specific rule and access validation results.

A rule is defined as an array containing two elements:

  1. The state to validate.
  2. A function that receives this state, and returns a boolean (true for succesful validation or false for failed validation).

Validate

The hook returns a validate function that can be used to run validation.

Validation can be triggered in several ways:

  • Calling validate() (with no arguments) will run validation against all rules using current state.
  • Calling validate('ruleName') (passing a rule name) will run validation against the specified rule using current state.
  • Calling validate('ruleName', state) (passing both a rule name and state) will run validation against the specified rule using the passed state.

Alternatively, see automatic validation.

Inspect results

The hook returns an isValid function and an isInvalid function that can be used to get validation results.

For a rule "myRule":

  • If isValid('myRule') === true the rule is valid.
  • If isInvalid('myRule') === true the rule is invalid.
  • If isValid('myRule') === false && isInvalid('myRule') === false the rule has not yet been validated.

For an explanation as to why results are defined in this way, see "Why two result functions?".

Configuration

react-use-validation can be configured by passing an options object as the second argument:

const validation = useValidation(rules, options)

Default configuration is as follows:

{
  validateOnInit: false,
  validateOnChange: false,
}

validateOnInit

Defaults to false

By default, react-use-validation does not validate on initialization (see "Why not validate on initial render?").

To validate on initialization, set validateOnInit to true.

validateOnChange

Defaults to false

By default, validation is only triggered by calling the validate function.

For convenience, you can set rules to automatically validate every time their state values change by setting validateOnChange to true.

State change is detected through strict deep value equality (not referential equality).

API

react-use-validation returns an object with the following members:

isValid

A function that accepts an optional rule name string and returns a boolean that determines if the rule/rules are valid. If a rule name is provided, only the specified rule will be checked. If no rule name is provided, all rules will be checked.

Executing this function does not run validation.

const isValid: <TRuleKey extends keyof TRules>(ruleKey?: TRuleKey | undefined) => boolean

isInvalid

A function that accepts an optional rule name string and returns a boolean that determines if the rule/rules are invalid. If a rule name is provided, only the specified rule will be checked. If no rule name is provided, all rules will be checked.

Executing this function does not run validation.

const isInvalid: <TRuleKey extends keyof TRules>(ruleKey?: TRuleKey | undefined) => boolean

validate

A function that accepts an optional rule name string and an optional value to validate the rule against, and returns a boolean that determines if the rule/rules are valid (true) or invalid (false). If a rule name is provided, only the specified rule will be validated. If a value is provided, the specified rule will be validated using the supplied value. If no rule name is provided, all rules will be validated using current state values.

Executing this function runs validation and may update state.

const validate: <TRuleKey extends keyof TRules>(
  ruleKey?: TRuleKey | undefined,
  state?: TRules[TRuleKey][0] | undefined
) => boolean

FAQs

Why not validate on initial render?

The most common use-case for validation is validating form input values. Most input values are invalid (empty) by default, so validating everything on initial render means a user sees error messages before they do anything.

To make react-use-validation easier to work with, a validation rule does not run until its state value changes from the initial value.

You can configure this with options.validateOnInit.

Why two result functions?

As validation does not happen on initialization by default (see "Why not validate on initial render?"), a rule can be in one of three states: valid, invalid, or unvalidated.

The most common use-case for reading validation results is to render an error message or user hint. If a result had three possible values (true, false, or undefined) you would need to differentiate between false and undefined when rendering the error:

// Strict equality check is required
validation.getResult('nameEntered') === false && <div>{'Enter a name'}</div>

Instead, with the current implementation we can write:

validation.isInvalid('nameEntered') && <div>{'Enter a name'}</div>

The first example is more verbose, and may lead into the trap of writing !validation.getResult('nameEntered') which would incorrectly render an error if validation had not run (validation result of undefined).

The current implementation is more declarative, shorter, and easier to read.

Examples

Input validation

Validating a single input is as easy as defining a validation rule, validating when state changes, and showing a hint if validation fails:

const [name, setName] = React.useState('')

const validation = useValidation({
  // Rule only passes if name has been entered
  nameEntered: [name, n => Boolean(n.length)],
})

return (
  <div>
    <label>{'Name'}</label>
    <input
      value={name}
      onChange={e => {
        // Validate using an up-to-date state value
        validation.validate('nameEntered', e.target.value)
        setName(e.target.value)
      }}
    />
    {validation.isInvalid('nameEntered') && <div>{'Enter a name'}</div>}
  </div>
)

Form validation

In a form where several fields require validation, we want to validate fields individually as their values change, and then validate all fields at once before submitting.

validate returns a boolean true for successful validation, or false for failed validation.

Call validate inside your onSubmit function, or call the isValid function or validate function (note that calling validate is preferred as this also triggers validation):

const [name, setName] = React.useState('')
const [email, setEmail] = React.useState('')

const validation = useValidation({
  nameEntered: [name, n => Boolean(n.length)],
  emailShape: [email, e => /.+@.+\..+/.test(e)],
})

const onSubmit = e => {
  e.preventDefault()

  // Validate all rules before submitting
  const isFormValid = validation.validate()

  // Alternatively, check that all rules are marked as valid
  // const isFormValid = validation.isValid()

  if (isFormValid) {
    // Submit form here...
  }
}

return (
  <form onSubmit={onSubmit}>
    <div>
      <label>{'Name'}</label>
      <input
        value={name}
        onChange={e => {
          // Validate name on change
          validation.validate('nameEntered', e.target.value)
          setName(e.target.value)
        }}
      />
      {validation.isInvalid('nameEntered') && <div>{'Enter a name'}</div>}
    </div>
    <div>
      <label>{'Email'}</label>
      <input
        value={email}
        onChange={e => {
          // Validate email on change
          validation.validate('emailShape', e.target.value)
          setEmail(e.target.value)
        }}
      />
      {validation.isInvalid('emailShape') && <div>{'Enter a valid email'}</div>}
    </div>
    <button>Submit</button>
  </form>
)

Complex state validation

Sometimes a single rule needs to listen to multiple state values. In this case, pass both values to the rule as either an object or an array:

const [password, setPassword] = React.useState('')
const [confirmPassword, setConfirmPassword] = React.useState('')

const validation = useValidation({
  passwordComplexity: [password, p => p.length > 7],
  // Pass an object containing both state values
  passwordMatch: [{ p: password, cp: confirmPassword }, ({ p, cp }) => p === cp],
  // An array also works
  // passwordMatches: [[password, confirmPassword], ([p, cp]) => p === cp],
})

return (
  <div>
    <div>
      <label>{'Password'}</label>
      <input
        value={password}
        onChange={e => {
          validation.validate('passwordComplexity', e.target.value)
          setPassword(e.target.value)
        }}
      />
      {validation.isInvalid('passwordComplexity') && (
        <div>{'Password must be at least 8 characters'}</div>
      )}
    </div>
    <div>
      <label>{'Confirm Password'}</label>
      <input
        value={confirmPassword}
        onChange={e => {
          // Make sure to pass the correct state object shape
          validation.validate('passwordMatch', { p: password, cp: e.target.value })
          setConfirmPassword(e.target.value)
        }}
      />
      {validation.isInvalid('passwordMatch') && <div>{'Password does not match'}</div>}
    </div>
  </div>
)

Automatic validation

With automatic validation, there's no need to call validate in the onChange function (when validating prior to form submission, validate should still be called).

const [name, setName] = React.useState('')

const validation = useValidation(
  {
    nameEntered: [name, n => Boolean(n.length)],
  },
  {
    // Automatic validation when state changes
    validateOnChange: true,
  }
)

return (
  <div>
    <label>{'Name'}</label>
    <input
      value={name}
      // No need to call validate here
      onChange={e => setName(e.target.value)}
    />
    {validation.isInvalid('nameEntered') && <div>{'Enter a name'}</div>}
  </div>
)