Asynchronous validation
Most common async validation is a network request checking with the backend if the current value is available, like username or email, but it can be anything.
Building upon the previous example, we are adding an async validator to our form configuration object. Async validators are Promises that resolve with either an Error object or undefined.
Returned validations
object contains validation state information for your form fields. In the below example, alongside rendering an error message,
it is used to indicate a loading state during "username" validation.
Also, notice that our password field gets validated with Regex.
import { useForm } from "@formeus/react"
function SignIn({ someState }) {
const { values, update, submit } = useForm({
initial: {
username: "",
password: "",
},
validators: {
username: ({ username }) =>
username.length == 0
? new Error("Must contain at least 1 char.")
: undefined,
password: ({ password }) =>
/^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9]).{5,})\S/.test(password)
? undefined
: new Error(
"1 uppercase, 1 lowercase, 1 number and at least 6 chars."
),
},
asyncValidators: {
username: ({ username }, signal) => {
// username is already client side validated here
return api
.checkUsernameAvailable(username, signal)
.then((isAvailable) =>
isAvailable ? undefined : new Error("username not available")
)
},
},
onSubmitForm: ({ username, password }, signal, meta) => {
// someState in the line below will have an up to date value,
// even if it was undefined on initial render
const { someState } = meta
api.signIn(username, password)
},
meta: {
someState,
},
})
return (
<>
<input
value={values.username}
onChange={(e) => update("username", e.target.value)}
/>
<label>{validations.username.error?.message}</label>
{validations.username.validating && <span>Loading...</span>}
<input
value={values.password}
onChange={(e) => update("password", e.target.value)}
/>
<label>{validations.password.error?.message}</label>
<button onClick={() => submit()}>Submit</button>
</>
)
}
validators always run before asyncValidators, preventing unecessary requests for something that can be checked on the client. (like an empty field).
UX considerations
To provide the best user experience when executing an asynchronous validation, make use of
the returned validations
object in conjuction with isValid
and isValidating
flags to achieve the desired
form behaviour.
Most obvious usage of isValid
flag is to control the button state, which can perhaps remain disabled as long as the form isValid = false
.
With that setup, user can't trigger the validation process with the submit
method so make use of the autoValidate
form configuration option to
automatically run validation when the field is updated.