@damilaredev/react-form-validation-hook
Version:
React Form Validation
237 lines (232 loc) • 7.61 kB
JavaScript
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 };