React hooks for managing form state. Uses React Hooks API to manage common form elements state. The hooks are meant to be generic and usefull.
Requires react@^16.8.0 react-dom@^16.8.0
as peer dependencies.
- Keeps field value state
- Keeps field touched state
- Keeps field validation state
- Executes validation and exposes validation errors
- Will support async validation (TODO)
npm
npm i --save react-use-form-hooks
yarn
yarn add react-use-form-hooks
Example:
import React from 'react';
import { useFormField, useFormWithFields } from 'react-use-form-hooks';
export default () => {
const fieldOne = useFormField({
initialValue: '',
isRequired: true,
validate: doValidationSomehow
});
const fieldTwo = useFormField();
const form = useFormWithFields({
onSubmit: doSomethingWithAcceptableFormData,
fields: [fieldOne, fieldTwo]
});
return (
<form onSubmit={form.handleSubmit}>
<MyInput
label="Field One"
type="text"
value={fieldOne.value}
required={fieldOne.isRequired}
onChange={fieldOne.handleChange}
isValid={fieldOne.isValid}
isTouched={fieldOne.isTouched}
validationErrors={fieldOne.validationErrors}
successfulValidations={fieldOne.successfulValidations}
/>
<MyInput
label="Field Two"
type="can be anything really"
value={fieldTwo.value}
required={fieldTwo.isRequired}
onChange={fieldTwo.handleChange}
isValid={fieldTwo.isValid}
isTouched={fieldTwo.isTouched}
validationErrors={fieldTwo.validationErrors}
successfulValidations={fieldTwo.successfulValidations}
/>
<button type="submit" disabled={!form.isSubmittable}>
Submit
</button>
</form>
);
}
The module exports two hooks: useFormField
and useFormWithFields
.
const field = useFormField(params);
Returns object with following fields:
{
isEmpty, // boolean; indicates whether field value is empty
isRequired, // boolean
isTouched, // boolean; indicates whether field value has been changed
isValid, // boolean; indicates whether field value is valid
isAcceptable, // boolean; indicates whether field is known to be OK to be used in form data submission
handleChange, // function; should be called on controlled input change if it should be validated immediately
handleChangeValueOnly, // function; should be called on controlled input change if it should NOT be validated immediately. Field will be validated on blur or submission attempt instead
handleReset, // function; reset field to initial state
handleClear, // function; clear field value
handleSubmitAttempt, // function: Called by useFormWithFields hook. May be called manually on form submit attempt if useFormWithFields is not used
successfulValidations, // array of successful validations results
validationErrors, // array of failed validation results / validation error messages
value //any; field value
}
{
accessor, // function or string, optional
adapter, // function, optional
emptyValue, // any, optional
initialValue, // any, optional, default is empty string
isRequired, // boolean, optional, default is false
validate, // function, optional
validateRequired // function, optional
}
Optional. Used to extract field value out of object passed as a parameter to handleChange
and handleChangeValueOnly
handlers.
If accessor is a string, it is used as a path to object's value.
If accessor is a function it is expected to return the new value.
Note: if no acessor is provided, the hook will first attempt to treat parameter as event and resolve it's 'target.value' path. In case if 'target.value' path returns undefined, the raw param will used as value.
Optional. Used to supplement field values. For example, to produce a shape that can be fed to bootstap components. The function only needs to return the new fields.
Optional. Value that will be set on field clear with handleClear
.
Optional. Defaults to empty string. Initial value. Will also be set on field reset with handleReset
.
Optional. If true, field will be treated as not OK for submission when empty.
Optional. Function that is used to validation the field.
Optional. Function that is used to check if field is empty. Should return error message or any truthy value if field is empty and a falsy value otherwise. Defaults to a function that returns truthy for undefined
, null
, ''
and [] values.
const field1 = useFormField(params);
const field2 = useFormField(params);
const form = useFormWithFields({
onSubmit: submitValidFormDataSomehow,
fields: [field1, field2]
});
Returns object with following fields:
{
isSubmittable, // boolean; indicates whether all form fields are OK
isSubmitAttempted, // boolean; indicates whether handleSubmit has been called at least once
handleSubmit // function to be called on form submission attempt
}
{
onSubmit, // function; called on submit attempt if all fields are OK
onFailedSubmit, // function; called on submit attempt if at least one field is not OK
fields // array of return values of useFormField hooks
}
Optional. If supplied, it will be called with the param (usually event) passed to the handleSubmit
handler only if all fields listed in fields
param are OK.
Optional. If supplied, it will be called with the param (usually event) passed to the handleSubmit
handler only if at least one field listed in fields
param is not OK.
Defaults to function that prevents default event handling.
Required. Array of objects produced by useFormField
hooks.
validate
function may return an error message if validation fails and any falsy value if it succeeds.
Example:
validate: value => value.length < 5
? 'Value must be at least 5 characters long'
: null
When above validation fails, the field shape will have keys:
{
successfulValidations: [],
validationErrors: ['Value must be at least 5 characters long'],
isInvalid: true,
isValid: false,
...other
}
When above validation succeeds, the field shape will have keys:
{
successfulValidations: [],
validationErrors: [],
isInvalid: false,
isValid: true,
...other
}
Note: since in this form validate
only returns errorMessage on failure, successfulValidations
will always be an empty array.
There may be multiple problems with the value that is being validated and it may be a good idea to let user know of all of them instead of presenting only one at a time.
validate
function may return an array of validation error messages.
A good example is a password field:
validate: password => {
const errorMessages = [];
if (password.length < 5) {
errorMessages.push('Password must be at least 5 characters long');
}
if (!RegExp(/.*[A-Z]+.*/g).test(password)) {
errorMessages.push('Password must include at least one uppercase letter');
}
if (!RegExp(/.*[a-z]+.*/g).test(password)) {
errorMessages.push('Password must include at least one lowercase letter');
}
return errorMessages;
}
When above validation fails, the field shape will have keys:
// ex. value = 'test';
{
successfulValidations: [],
validationErrors: [
'Password must be at least 5 characters long',
'Password must include at least one uppercase letter'
],
isInvalid: true,
isValid: false,
...other
}
When above validation succeeds, the field shape will have keys:
{
successfulValidations: [],
validationErrors: [],
isInvalid: false,
isValid: true,
...other
}
Note: since in this form validate
only returns errorMessages, successfulValidations
will always be an empty array.
If you would like to be explicit about what validations suceeded, validate
function may return an array with two items in it: [validationErrors, successfulValidations]
.
First item is an array of validation error messages.
The second item is an array of successful validation messages.
Example:
validate: password => {
const errorMessages = [];
const successfulValidations = [];
if (password.length < 5) {
errorMessages.push('Password must be at least 5 characters long');
} else {
successfulValidations.push('Password is long enough');
}
if (!RegExp(/.*[A-Z]+.*/g).test(password)) {
errorMessages.push('Password must include at least one uppercase letter');
} else {
successfulValidations.push('Password has an uppercase letter');
}
if (!RegExp(/.*[a-z]+.*/g).test(password)) {
errorMessages.push('Password must include at least one lowercase letter');
} else {
successfulValidations.push('Password has a lowercase letter');
}
return [errorMessages, successfulValidations];
}
When above validation fails, the field shape will have keys:
// ex. value = 'test';
{
successfulValidations: [
'Password has a lowercase letter'
],
validationErrors: [
'Password must be at least 5 characters long',
'Password must include at least one uppercase letter'
],
isInvalid: true,
isValid: false,
...other
}
When above validation succeeds, the field shape will have keys:
{
successfulValidations: [
'Password is long enough',
'Password has an uppercase letter',
'Password has a lowercase letter'
],
validationErrors: [],
isInvalid: false,
isValid: true,
...other
}
In some cases validation requires an async operation. For example, making an API call to check if username is taken.
COMING SOON
Sometimes, field validation depends on another field. A simple example is a password repeat field that needs to compare repeat field value to password field value.
This is where react hooks
shine: if both fields are in scope of the same functional component, value of one field can be accessed in validate
function of another.
Example:
function PasswordsMatch(props) {
const password = useFormField(); // no validation for simplicity
const passwordRepeat = useFormField({
validate: value => value !== password
? 'Passwords must match'
: null
});
}
To validate both fields:
function PasswordsMatch(props) {
const password = useFormField({
validate: validateMatchesPasswordRepeat
});
const passwordRepeat = useFormField({
validate: validateMatchesPassword
});
function validateMatchesPasswordRepeat(value) {
return value !== passwordRepeat
? 'Passwords must match'
: null;
}
function validateMatchesPassword(value) {
return value !== password
? 'Passwords must match'
: null;
}
}
yarn
git clone https://github.com/AntonRublev360/react-use-form-hooks.git
cd react-use-form-hooks
yarn install
yarn start
npm
git clone https://github.com/AntonRublev360/react-use-form-hooks.git
cd react-use-form-hooks
npm i
npm start
MIT