Validation

The Form component takes a validator as a prop, this is an object that must contain a validate function and an optional validateAttribute function.

The validate function will take the form data and return an ErrorBag populated with a list of errors this form data has. The validateAttribute function will take an attribute and the form data and must return an array of error messages corresponding to the attribute.

React Form comes with a built in validator that will handle most of the heavy lifting for you.

const validator = createValidator({
username: [
({ title }) => {
if (!title || title.length === 0) {
return "Title is required";
}
},
],
password: [
({ password }) => {
if (!password || password.length === 0) {
return "Password is required";
}
},
({ password }) => {
if (password && password.length < 3) {
return "Password must have more than 3 characters";
}
},
],
});

Each key is an attribute notation that will correspond to an attribute in formState. Each value is an array of functions that will take form state and return an error message if the validation fails. If an empty string or undefined is returned, that validation check is considered successful.

The validator supports nested attribute validation with JSON Path style deep scan. To define a nested validator you add the attribute with a .. for example to validate a users first name you can use users..firstname

For these types of rules, you can use the validation options you get passed into a validation function. For more info on validation options you can see the ValidationOptions type in validator.ts

const validator = createValidator({
'users..firstname': [
(_, { value, path, attribute }) => {
if (!value || value.length === 0) {
return "Title is required";
}
},
],
});

Validation objects can also be cloned, so you can add extra rules. This can be used to add extra validation that will only run on the server. On the client validator you will get all the validation rules that can be run in the browser, in the server validator you get all the rules from the client and extra ones that can access databases to-do extra checks.

const clientValidator = createValidator({
email: [(({ email }) => isEmpty(email) && "Email cannot be blank")]
})
const serverValidator = clientValidator
.clone()
.addRule('email', ({ email }) => isInDb(email) && "Email must be unique")

Async validators are also allowed if you need to await http or database calls when building validators.

const validator = createValidator({
url: [
async ({ url }) => {
const result = await fetch(url, { method: "HEAD" });
if (result.status >= 400) {
return `${url} is not accessible`;
}
},
],
});