@modular-forms/react
Version:
The modular and type-safe form library for React
101 lines (100 loc) • 4.39 kB
JavaScript
import { batch } from '@preact/signals-react';
import { getFilteredNames, getOptions, getUniqueId, getFieldStore, getFieldArrayStore, setErrorResponse, updateFormInvalid, } from '../utils';
import { focus } from './focus';
import { getValues } from './getValues';
export async function validate(form, arg2, arg3) {
// Filter names between field and field arrays
const [fieldNames, fieldArrayNames] = getFilteredNames(form, arg2);
// Destructure options and set default values
const { shouldActive = true, shouldFocus = true } = getOptions(arg2, arg3);
// Create unique validator ID and add it to list
const validator = getUniqueId();
form.internal.validators.add(validator);
// Set validating to "true"
form.validating.value = true;
// Run form validation function
const formErrors = form.internal.validate
? await form.internal.validate(getValues(form, { shouldActive, peek: true }))
: {};
// Create valid variable
let valid = typeof arg2 !== 'string' && !Array.isArray(arg2)
? !Object.keys(formErrors).length
: true;
const [errorFields] = await Promise.all([
// Validate each field in list
Promise.all(fieldNames.map(async (name) => {
// Get store of specified field
const field = getFieldStore(form, name);
// Continue if field corresponds to filter options
if (!shouldActive || field.active.peek()) {
// Create local error variable
let localError;
// Run each field validation functions
for (const validation of field.validate) {
localError = await validation(field.value.peek());
// Break loop if an error occurred
if (localError) {
break;
}
}
// Create field error from local and global error
const fieldError = localError || formErrors[name] || '';
// Set valid to "false" if an error occurred
if (fieldError) {
valid = false;
}
// Update error state of field
field.error.value = fieldError;
// Return name if field has an error
return fieldError ? name : null;
}
})),
// Validate each field array in list
Promise.all(fieldArrayNames.map(async (name) => {
// Get store of specified field array
const fieldArray = getFieldArrayStore(form, name);
// Continue if field array corresponds to filter options
if (!shouldActive || fieldArray.active.peek()) {
// Create local error variable
let localError = '';
// Run each field array validation functions
for (const validation of fieldArray.validate) {
localError = await validation(fieldArray.items.peek());
// Break loop and if an error occurred
if (localError) {
break;
}
}
// Create field array error from local and global error
const fieldArrayError = localError || formErrors[name] || '';
// Set valid to "false" if an error occurred
if (fieldArrayError) {
valid = false;
}
// Update error state of field
fieldArray.error.value = fieldArrayError;
}
})),
]);
batch(() => {
// Set error response if necessary
setErrorResponse(form, formErrors, { shouldActive });
// Focus first field with an error if specified
if (shouldFocus) {
const name = errorFields.find((name) => name);
if (name) {
focus(form, name);
}
}
// Update invalid state of form
updateFormInvalid(form, !valid);
// Delete validator from list
form.internal.validators.delete(validator);
// Set validating to "false" if there is no other validator
if (!form.internal.validators.size) {
form.validating.value = false;
}
});
// Return whether fields are valid
return valid;
}