UNPKG

@damilaredev/react-form-validation-hook

Version:
237 lines (232 loc) 7.61 kB
import { useReducer } from 'react'; // src/hooks/useValidation.ts // src/utils/validationRules.ts var ValidationRules = { email: { rule: (value) => { return value && value.toString().match( /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i ); }, formatter: (fieldName) => `${fieldName} is not valid` }, required: { rule: (value) => value && value !== void 0 && value.toString().match(/\S/), formatter: (fieldName) => fieldName ? `${fieldName} is required` : "This field is required." }, numeric: { rule: (value) => value && value.toString().match(/^\d+$/), formatter: (fieldName) => `${fieldName} should contain only numbers.` }, alphaNumeric: { rule: (value) => value && value.toString().match(/^[a-z0-9]+$/i), formatter: (fieldName) => `${fieldName} should not contain special characters, please use only alphabets and numbers.` }, alphabetic: { rule: (value) => value && value.toString().match(/^[a-z]+$/i), formatter: (fieldName) => `${fieldName} should contain only alphabets.` }, maxLength: { rule: (value, number) => value && value.toString().length <= number, formatter: (fieldName, number) => { return number ? `${fieldName} can contain maximum ${number} characters.` : `${fieldName} contains more characters than expected.`; } }, minLength: { rule: (value, number) => value && value.toString().length >= number, formatter: (fieldName, number) => number ? `${fieldName} should contain minimum ${number} characters.` : `${fieldName} contains less characters than expected.` }, phone: { rule: (value) => value && value.toString().match(/^(\+|)(234|0)(7|8|9)(0|1)\d{8}$/), formatter: (value) => `${value} should contain valid phone number` }, url: { rule: (value) => value && value.toString().match( /https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,}/ ), formatter: (fieldName) => fieldName ? `${fieldName} must be valid url` : "This field must be a valid url" }, in: { rule: (value, params) => value && params.includes(value.toString()), formatter: (fieldName, params) => `${fieldName} not in any of ${params}` }, notIn: { rule: (value, params) => value && !params.includes(value.toString()), formatter: (fieldName, params) => `${fieldName} in any of ${params}` }, digitsBetween: { rule: (value, params) => value && value.toString().length >= params[0] && value.toString().length <= params[1], formatter: (fieldName, params) => `${fieldName} must be a number and between ${params[0]} and ${params[1]}` }, regex: { rule: (value, regex) => value && value.toString().match(regex), formatter: (fieldName) => `${fieldName} does not match the regex provided` }, size: { rule: (value, param) => { return value && value.toString().length === param; }, formatter: (fieldName, param) => `${fieldName} must be exactly ${param} characters long` }, cardNumber: { rule: (value) => { return value && value.toString().match( /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|62[0-9]{14})$/ ); }, formatter: (fieldName) => `${fieldName} is not a valid card number` } }; var validationRules_default = ValidationRules; // src/hooks/useValidation.ts var validate = ({ key, value, values, checks, customValidator }) => { if (checks) { const rules = checks.split("|"); let errorMessage = ""; if (rules.length) { rules.forEach((rule) => { const ruleType = rule.split(":"); const ruleName = ruleType.shift(); const params = ruleType.length ? ruleType.pop().split(",") : []; const validation = validationRules_default[ruleName]; let testArguments = []; let formatterArguments = []; switch (ruleName) { case "maxLength": case "minLength": case "regex": case "size": testArguments = [value, params[0]]; formatterArguments = [key, params[0]]; break; case "in": case "notIn": case "digitsBetween": case "creditCard": testArguments = [value, params]; formatterArguments = [key, params]; break; default: testArguments = [value]; formatterArguments = [key]; break; } if (ruleName.match(/nullable/) && (!value || value.constructor === Array && value.length < 1)) { return true; } const isRuleSatisfied = ruleName !== "required" && !value ? true : !!validation.rule.apply(null, testArguments); if (!isRuleSatisfied) { errorMessage = validation.formatter.apply(null, formatterArguments); } }); } return errorMessage; } return typeof customValidator === "function" && customValidator(value, values); }; var reducer = (state, action) => { switch (action.type) { case "UPDATE_FIELD": return { ...state, errors: { ...state.errors, [action.payload.key]: validate({ key: action.payload.key, value: action.payload.value, values: state.values, checks: state.checks[action.payload.key], customValidator: state.validators[action.payload.key] }) }, values: { ...state.values, [action.payload.key]: action.payload.value } }; } }; var useFormValidator = (inputs) => { const initial = { checks: {}, values: {}, validators: {}, errors: {}, messages: {} }; for (const key in inputs) { initial.checks[key] = inputs[key].checks; initial.validators[key] = inputs[key].validate; initial.values[key] = inputs[key].value; initial.errors[key] = ""; } const [fields, setFormField] = useReducer(reducer, initial); const validateField = ({ key, value }) => { if (fields.values[key] === void 0) { throw Error(`Field with key "${key}" not found, please make sure it is define in as follows: useFormValidator({ ${key}: { value: "", checks: "required" } }) `); } setFormField({ type: "UPDATE_FIELD", payload: { key, value } }); }; const isFieldValid = (key) => { validateField({ key, value: fields.values[key] }); if (fields.errors[key]) return false; return true; }; const updateField = (e) => { validateField({ key: e.target.name, value: e.target.value }); }; const isAllFieldsValid = () => { let valid = true; for (const key in fields.values) { const error = validate({ value: fields.values[key], checks: fields.checks[key] }); if (error) valid = false; setFormField({ type: "UPDATE_FIELD", payload: { key, value: fields.values[key], checks: fields.checks[key] } }); } return valid; }; return { values: fields.values, errors: fields.errors, isAllFieldsValid, isFieldValid, updateField }; }; export { useFormValidator, validate };