react-native-form-validator
Version:
React native library to validate form fields
159 lines (143 loc) • 4.88 kB
JavaScript
import PropTypes from 'prop-types';
import { useState, useRef } from 'react';
import defaultMessages from './messages/defaultMessages';
import defaultRules from './rules/defaultRules';
const useValidation = (props) => {
const { state = {} } = props;
// array to store error on each fields
// ex:
// [{ fieldName: "name", messages: ["The field name is required."] }]
// Retrieve props
const deviceLocale = props.deviceLocale || 'en'; // ex: en, fr
const baseRules = props.rules || defaultRules; // rules for Validation
const messages = props.messages || defaultMessages;
const labels = props.labels || {};
const errors = useRef([]);
const [internalErrors, setInternalErrors] = useState([]);
/*
* Method validate to verify if each children respect the validator rules
* Fields example (Array) :
* fields = {
* input1: {
* required:true,
* numbers:true,
* maxLength:5
* }
*}
*/
const validate = (fields) => {
// Reset errors
_resetErrors();
// Iterate over inner state
for (const key of Object.keys(state)) {
// Check if child name is equals to fields array set up in parameters
const rules = fields[key];
if (rules) {
// Check rule for current field
_checkRules(key, rules, state[key]);
}
}
return isFormValid();
};
// Method to check rules on a spefific field
const _checkRules = (fieldName, rules, value) => {
if (!value && !rules.required) {
return; // if value is empty AND its not required by the rules, no need to check any other rules
}
for (const key of Object.keys(rules)) {
const isRuleFn = typeof baseRules[key] == 'function';
const isRegExp = baseRules[key] instanceof RegExp;
if ((isRuleFn && !baseRules[key](rules[key], value)) || (isRegExp && !baseRules[key].test(value))) {
_addError(fieldName, key, rules[key], isRuleFn);
}
}
};
// Add error
// ex:
// [{ fieldName: "name", messages: ["The field name is required."] }]
const _addError = (fieldName, rule, value, isFn) => {
const name = labels[fieldName];
value = rule == 'minlength' ? value - 1 : value;
const errMsg = messages[deviceLocale][rule].replace('{0}', name || fieldName).replace('{1}', value);
let [error] = errors.current.filter((err) => err.fieldName === fieldName);
// error already exists
if (error) {
// Update existing element
const index = errors.current.indexOf(error);
error.messages.push(errMsg);
error.failedRules.push(rule);
errors.current[index] = error;
} else {
// Add new item
errors.current.push({
fieldName,
failedRules: [rule],
messages: [errMsg]
});
setInternalErrors(errors.current);
}
};
// Reset error fields
const _resetErrors = () => {
errors.current = [];
setInternalErrors([]);
};
// Method to check if the field is in error
const isFieldInError = (fieldName) => {
return internalErrors.filter((err) => err.fieldName === fieldName).length > 0;
};
const isFormValid = () => {
return errors.current?.length == 0;
};
// Return an object where the keys are the field names and the value is an array with the rules that failed validation
const getFailedRules = () => {
let failedRulesPerField = {};
for (let index = 0; index < internalErrors.length; index++) {
let error = internalErrors[index];
failedRulesPerField[error.fieldName] = error.failedRules;
}
return failedRulesPerField;
};
// Return the rules that failed validation for the given field
const getFailedRulesInField = (fieldName) => {
const foundError = internalErrors.find((err) => err.fieldName === fieldName);
if (!foundError) {
return [];
}
return foundError.failedRules;
};
// Concatenate each error messages
const getErrorMessages = (separator = '\n') => {
return internalErrors.map((err) => err.messages.join(separator)).join(separator);
};
// Method to return errors on a specific field
const getErrorsInField = (fieldName) => {
const foundError = internalErrors.find((err) => err.fieldName === fieldName);
if (!foundError) {
return [];
}
return foundError.messages;
};
return {
validate,
isFormValid,
isFieldInError,
getFailedRules,
getFailedRulesInField,
getErrorMessages,
getErrorsInField
};
};
useValidation.propTypes = {
deviceLocale: PropTypes.string, // Used for language locale
rules: PropTypes.object, // rules for validations
messages: PropTypes.object, // messages for validation errors
labels: PropTypes.object // labels for validation messages
};
useValidation.defaultProps = {
deviceLocale: 'en',
rules: defaultRules,
messages: defaultMessages,
labels: {}
};
export default useValidation;