README
cl-use-form-state
React form state and validation hook with great TypeScript support.
There are no dependencies besides React as a peer dependency.
Check out this repository for a complete Form component built on top of this library and MaterialUI v.4.
Install
yarn add cl-use-form-state
Usage
this sandbox for an example
Check outimport React from "react";
import { useForm } from "cl-use-form-state";
type FormInputs = {
username: string;
password: string;
age: number;
};
export function Component() {
const {
// an object with the current state of the inputs
inputs,
// a boolean that is true if all inputs are valid
isValid,
// returns an object with every input key along with its current value
getInputValues,
// react on change event handler i.e onChange
onChangeHandler,
// react on focus event handler i.e onBlur
onTouchHandler,
// optional function to update input value
updateInput,
// optional function to (re)set entire form state
setFormState,
} = useForm<FormInputs>((cl) => {
/* useForm takes a function as its argument and that function
receives another function that can be used to create inputs
All defined inputs must be present in the returned object */
return {
username: cl("", { minLength: 1, maxLength: 32 }),
password: cl("", {
minLength: 8,
maxLength: 64,
minNumericalSymbols: 1,
minUppercaseCharacters: 1,
}),
age: cl(20, { minValue: 18 }),
};
});
return <>{...yourJsx}</>;
}
cl
cl
takes two arguments. An initial value and an object with options for the created input.
name | type | default | note |
---|---|---|---|
isValid |
boolean |
false |
- |
isTouched |
boolean |
false |
- |
minLength |
number |
undefined |
- |
maxLength |
number |
undefined |
- |
minValue |
number |
undefined |
- |
maxValue |
number |
undefined |
- |
minUppercaseCharacters |
number |
undefined |
- |
maxUppercaseCharacters |
number |
undefined |
- |
minNumericalSymbols |
number |
undefined |
- |
maxNumericalSymbols |
number |
undefined |
- |
customRule |
(value: InputValueType, state: FormState) => boolean |
undefined |
can be used to create any validation rule for any input field, see here |
connectFields |
string[] |
undefined |
can be used to make fields dependant upon each other, see here |
updateInput
updateInput
can be used to update the value of an input if onChangeHandler
cannot be used.
It takes two arguments, the id
of the input to change and the new value
of that input.
The type of the value
argument is inferred from the id
. Say we have these simple input types
type FormInputs = {
name: string;
age: number;
};
and we use updateInput
as an onClick
handler. Then this pattern follows:
// Ok
onClick={() => updateInput("name", "someValue")} // expects string
onClick={() => updateInput("age", 21)} // expects number
// Not ok
onClick={() => updateInput("name", 21)} // expects string
onClick={() => updateInput("age", "someValue")} // expects number
customRule
A customRule
must be a function that takes two arguments, value
and state
. The value will always be the newest value of the associated input field while the state always will be the newest state of the entire form.
Lets say we have an input where we'd only want to support any given username
that starts with C, ends with h and has a maximum length of the current age
value:
const form = useForm<{
name: string;
age: number | null;
}>((createInput) => {
return {
name: createInput("", {
customRule: (value, state) => {
const trimmedValue = value.trim();
const length = trimmedValue.length;
return (
length > 0 &&
length <= (state.inputs.age.value || 0) &&
trimmedValue[0] === "C" &&
trimmedValue[length - 1] === "h"
);
},
}),
age: createInput(null, { minValue: 1 }),
};
});
connectFields
The above customRule
example has an issue. Lets assume that we have a name
that starts with C, ends with h and has a maximum length of the age
value. Lets say the name
length is 12
and the age
value is 15
. Now name
is a valid input. However, if we change the age
value to say 10
, then the name
value is still valid although it no longer satisfies its own validation constraints.
In this scenario, we want a behavior where each time the value of age
changes, the validation for name
is re-run. We can achieve this using the connectFields
option, that just takes an id
of the input we'd like to connect.
const form = useForm<{
name: string;
age: number | null;
}>((createInput) => {
return {
name: createInput("", {
customRule: (value, state) => {
const trimmedValue = value.trim();
const length = trimmedValue.length;
return (
length > 0 &&
length <= (state.inputs.age.value || 0) &&
trimmedValue[0] === "C" &&
trimmedValue[length - 1] === "h"
);
},
}),
age: createInput(null, { minValue: 1, connectFields: ["name"] }),
};
});