@zzzzbov/usefield v0.1.1
useField react hook
useField is a react hook for managing synchronous field validation alongside state.
useField is meant for simple forms with a few fields and limited field validation needs.
Contents
When not to useField
- if your form has more than ~7 fields
- if your fields don't need validation
- if you need asynchronous field validation
- if you want to manage validation on complex data structures, such as deeply nested objects, or dynamic collections of field data.
Installation
npm install @zzzzbov/usefieldUsage
import React, { useCallback } from 'react'
// Import the useField hook along with any needed validators
import { useField, required } from '@zzzzbov/usefield'
// `useField` can be used within any react control.
// This one is an example login form.
export const LogInForm = ({
// The onSubmit property here is used as an example to externalize the
// submitted form data. This is done to keep the example brief but is not
// strictly necessary.
onSubmit
}) => {
// Call the `useField` hook passing in as few or as many validators as
// desired, along with an initial field value. In this case a single required
// field validator is used.
const username = useField({
required
}, '')
// `useField` can be called as many times as needed for as many fields as
// needed. While this makes managing data very straightforward, it may be too
// verbose for complex forms that have more than a handful of fields.
const password = useField({
required
}, '')
// Set up a callback for the form submission
const internalSubmit = useCallback((e) => {
e.preventDefault()
// Mark the fields as dirty since the fields might not have been edited yet
username.touch()
password.touch()
// Check that the fields are valid.
// Use the `valid` properties and NOT the `error` helpers as the error
// helpers will return false if the fields are clean.
if (username.valid && password.valid) {
// Handle the form submission behavior.
// This example uses an onSubmit property to keep things simple.
onSubmit(username.value, password.value)
}
}, [onSubmit, username, password])
return (
<form
method='POST'
// Bind the submit callback on the form to take advantage of native form
// submission behaviors, such as implicit form submission.
onSubmit={internalSubmit}>
<h1>Log In</h1>
<div className='Field'>
<label htmlFor='username'>Username:</label>
{/*
Display an error message to the user.
If multiple validators are used, it may be desirable to check each
validator explicitly, such as:
{ username.dirty && !username.validation.required && (
<p>Username is required</p>
) }
Alternatively, validator-specific messages may be left visible so
that their current state is visibly toggled as the user changes the
field value:
<p>
{ username.validation.required ? '☑ ' : '☐ ' }
Username is required
</p>
*/}
{ username.error && (
<p id='username-error'>Username is required</p>
) }
<input
// The `error` property can be used to visually indicate an issue to
// the user such as by toggling a class.
className={username.error ? 'error' : ''}
id='username'
name='u'
type='text'
aria-describedby='username-error'
// Pass the current value to the field
value={username.value}
// Bind the `set()` method to the field.
// Some extra boilerplate is used to access the current field value.
// If the field should be marked as dirty, call touch as well:
//
// onChange={e => {
// username.set(e.target.value)
// username.touch()
// }}
onChange={e => username.set(e.target.value)}
// `touch()` is bound to the blur handler so that the field is flagged
// as dirty only after the user has finished editing the field.
onBlur={username.touch}
/>
</div>
<div className='Field'>
<label htmlFor='password'>Password:</label>
{ password.error && (
<p id='password-error'>Password is required</p>
) }
<input
className={password.error ? 'error' : ''}
id='password'
name='p'
type='password'
aria-describedby='password-error'
value={password.value}
onChange={e => password.set(e.target.value)}
onBlur={password.touch}
/>
</div>
{/*
A single submit button is used here to submit the form.
If a reset button is also desirable, it may be added, and an `onReset`
handler should be added to the <form> to reset the fields:
onReset={() => {
username.reset()
password.reset()
}}
*/}
<button type='submit'>
Log In
</button>
</form>
)
}Visit https://zzzzbov.github.io/useField-demo/ for a live demo.
API
useField(validators, initialValue)
The useField hook accepts a map of validators and an initial value and returns an object with the current value, utility methods, and validation state.
validators
The validators parameter is an object of functions to validate the field.
Each validator takes a single parameter of the current value, and should return true when the provided value is valid, and false when the provided value is invalid.
initialValue
The initialValue parameter specifies the initial value for the field before any user interaction occurs. While the primary use-case for useField is for string values, any type may be used.
Return value
useField returns an object with the following members:
clean()
The clean method marks the field as having been cleaned, which sets the dirty property to false, but does not otherwise change the current value.
See also: dirty, reset(), and touch()
dirty
Type: boolean
Default: false
The dirty property indicates whether the user has interacted with the field for purposes of displaying validation.
See also: clean(), error, reset(), and touch()
error
Type: boolean
Warning:
Do not use the
errorproperty for checking field validation in a form submit handler, as the fields may not have been considereddirtywhen the submission event occurs.
The error helper property indicates whether the field is dirty and not valid. This helper is meant to be used to indicate to a user that the field has an issue that should be fixed.
reset()
The reset method sets the value property back to the originally provided initialValue and cleans the field (i.e. sets dirty to false).
See also: clean(), dirty, set(value), touch(), and value
set(value)
The set method sets the value property. It does not mark the field as dirty so that displaying validation may be delayed until a user is done with the given field.
touch()
The touch method marks the field as dirty, and does not change the field value.
See also: clean(), dirty, and reset()
valid
Type: boolean
The valid property indicates whether all of the provided validators are currently valid.
See also: validators
validation
Type: object
The validation property is an object of the results from each of the provided validators.
const example = useField({
numeric (value) {
return !isNaN(value)
}
}, '')
example.validation.numeric
// after example.set('123') becomes true
// after example.set('abc') becomes falseSee also: validation
value
The value property is the currently assigned field value.
See also: initialValue, reset(), and set(value)
Built-in validators
useField comes with a few built-in validator utilities which can be passed to the validators parameter of useField.
matches(regex)
matches creates a validator function to match the current value against a provided regular expression.
Usage:
import { useField, matches } from '@zzzzbov/usefield'
...
const example = useField({
lowercase: matches(/a-z/),
uppercase: matches(/A-Z/)
}, '')
example.validation.lowercase // false until the example field has a value that contains a lower case character
example.validation.uppercase // false until the example field has a value that contains an upper case charactermaxLength(length)
maxLength creates a validator function to verify that the current value is no longer than the provided length.
Usage:
import { useField, maxLength } from '@zzzzbov/usefield'
...
const example = useField({
max: maxLength(20)
}, '')
example.validation.max // true until the example field has a value longer than 20 charactersminLength(length)
minLength creates a validator function to verify that the current value is at least as long as the provided length.
Usage:
import { useField, minLength } from '@zzzzbov/usefield'
...
const example = useField({
min: minLength(5)
}, '')
example.validation.min // false until the example field has a value that's at least 5 characters longrequired
required is a validator function to verify that the current value is truthy (not 0, false, '', NaN, null, or undefined).
Usage:
import { useField, required } from '@zzzzbov/usefield'
...
const example = useField({
required
}, '')
example.validation.required // false until the example field has a value