Skip to content

Instantly share code, notes, and snippets.

@aliakakis
Last active August 9, 2024 08:20
Show Gist options
  • Save aliakakis/6f7c675e66a848689417ec8901a2500c to your computer and use it in GitHub Desktop.
Save aliakakis/6f7c675e66a848689417ec8901a2500c to your computer and use it in GitHub Desktop.
Hook Validation for React
import {useRef} from 'react';
import validators from './validators';
/* Usage
const [user, setUser] = useState({
username: '',
password: '',
});
const [isUsernameInvalid, usernameErrors] = useValidate(user.username, ['isEmpty']);
const [isPasswordInvalid, passwordErrors] = useValidate(user.password, ['isEmpty']);
With passing extra params to a check
const [isIpInvalid, ipErrors] = useValidate(ipAddress, ['isEmpty', ['isValidIpAddressOrHostname', false]]);
<Button
disabled={isUsernameInvalid}
>
{'Login'}
</Button>
Vanilla version example
const errors = validate(ipAddress, ['isEmpty']);
With passing extra params to a check
const errors = validate(ipAddress, ['isEmpty', ['isValidIpAddressOrHostname', false]]);
*/
/**
* useValidate hook
* @type {*[]}
* @param {string} value - The value to check against validations
* @param {(string | string[])[]} validations - Validations
* @return {[boolean, string[]]}
*/
const useValidate = (
value = '',
validations
) => {
/** @type {string[]} */
const errors = [];
const init = useRef(false); /* When tests run for the first time do not run the tests */
/** @param {boolean} initValue */
const setInit = (initValue) => {
init.current = initValue;
};
if (value.length) setInit(true);
if (!init.current) {
return [true, []];
}
if (!validations) return [true, []];
for (const validation of validations) {
let check = null;
if (validation instanceof Array) {
const [validator, param] = validation;
check = validators[validator](value, param);
} else {
check = validators[validation](value);
}
if (typeof check === 'string') errors.push(check);
}
return [!!errors.length, errors];
};
/* Vanilla version but fires check immediately */
/**
* useValidate hook
* @type {*[]}
* @param {string} value - The value to check against validations
* @param {(string | string[])[]} validations - Validations
* @return {string[]}
*/
export const validate = (
value = '',
validations
) => {
/** @type {string[]} */
const errors = [];
if (!validations) return [];
for (const validation of validations) {
let check = null;
if (validation instanceof Array) {
const [validator, param] = validation;
check = validators[validator](value, param);
} else {
check = validators[validation](value);
}
if (typeof check === 'string') errors.push(check);
}
return errors;
};
export default useValidate;
/**
* Available validators
* @type {Object.<string, function(string, [param: * | Object.<*, *>]): boolean | string>}
*/
const validators = {
isEmpty: (value = '') => !value && 'This field is mandatory',
isEqualWith: (value = '', valueToCompareWith = '') =>
value !== valueToCompareWith && 'Fields are not equal',
isValidIpv4: (value = '') => {
const ipRegex =
/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/g;
return !ipRegex.test(value) && 'This is not a valid ip v4';
},
isValidMacAddress: (value = '') => {
const ipRegex =
/^[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}$/g;
return !ipRegex.test(value) && 'This is not a valid mac address';
},
isPasswordStrong: (value = '') => {
const ipRegex =
/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{9,}$/g;
return (
!ipRegex.test(value) &&
'Length should be at least 9 characters. ' +
'Must contain uppercase letter. ' +
'Must contain lowercase letter. ' +
'Must contain digit. Must contain special character'
);
},
isValidIpAddressOrHostname: (value = '', multiple = true) => {
const ipRegex =
// eslint-disable-next-line max-len
/^\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|\b(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])\b$/g;
return !ipRegex.test(value) && (multiple ?
'You need one or more ip addresses and/or hostnames ' +
'e.g. 10.1.2.13, 10.1.2.14, dc1.yourdatacenter.com, dc2.yourdatacenter.com' :
'You need a valid ip address or hostname ' +
'e.g. 10.1.2.13 or dc1.yourdatacenter.com');
},
isValidIpAddressOrHostnameDomainSearch: (value = '') => {
const ipRegex =
// eslint-disable-next-line max-len
/^(^$)|(\b[a-zA-Z0-9]\b)|\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|\b(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.?)+([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])\b$/g;
return !ipRegex.test(value) &&
'You need one or more ip addresses and/or hostnames ' +
'e.g. 10.1.2.13, 10.1.2.14, dc1.yourdatacenter.com, dc2.yourdatacenter.com';
},
};
export default validators;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment