added arrays of validators

This commit is contained in:
cupcakearmy 2019-09-28 19:00:49 +02:00
parent f402146f87
commit 47748b568e
3 changed files with 76 additions and 26 deletions

View File

@ -7,10 +7,24 @@ const Index: React.FC = () => {
const { auto, form, errors } = useForm({ const { auto, form, errors } = useForm({
username: '', username: '',
email: '',
password: '' password: ''
}, { }, {
username: value => value.length > 3, username: value => value.length > 3,
password: /[\d]{1,}/ email: {
validator: /@/,
message: 'Must contain an @',
},
password: [
{
validator: /[A-Z]/,
message: 'Must contain an uppercase letter'
},
{
validator: /[\d]/,
message: 'Must contain a digit'
},
]
}) })
return ( return (
@ -21,8 +35,11 @@ const Index: React.FC = () => {
<input {...auto('username')} placeholder="Username" /> <input {...auto('username')} placeholder="Username" />
{errors.username && 'Must be longer than 3'} {errors.username && 'Must be longer than 3'}
<input {...auto('email')} placeholder="EMail" />
{errors.email}
<input {...auto('password')} placeholder="Password" type="password" /> <input {...auto('password')} placeholder="Password" type="password" />
{errors.password && 'Must contain a number'} {errors.password}
</form> </form>
) )

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import React, { useState, useEffect } from 'react'
@ -20,13 +20,26 @@ export type useFormValidatorObject = {
export type useFormValidator = useFormValidatorMethod | useFormValidatorObject export type useFormValidator = useFormValidatorMethod | useFormValidatorObject
export type useFormValidatorParameter = useFormValidator | useFormValidator[]
export const HTMLInputExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.value export const HTMLInputExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.value
export const HTMLCheckboxExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.checked export const HTMLCheckboxExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.checked
export const useForm = <T, U extends { [key in keyof T]: useFormValidator }, E extends { [key in keyof U]?: string }>(init: T, validators: Partial<U> = {}, options: useFormOptions = {}) => { function isFormValidatorObject(validator: useFormValidatorMethod | useFormValidatorObject): validator is useFormValidatorObject {
return validator.constructor.name === 'Object'
}
const defaultErrorMessage = (key: any) => `Error in ${key}`
export const useForm = <T, U extends { [key in keyof T]: useFormValidatorParameter }, E extends { [key in keyof U]?: string }>(init: T, validators: Partial<U> = {}, options: useFormOptions = {}) => {
const [form, setForm] = useState<T>(init) const [form, setForm] = useState<T>(init)
const [errors, setErrors] = useState<Partial<E>>({}) const [errors, setErrors] = useState<Partial<E>>({})
const [isValid, setIsValid] = useState(true);
useEffect(() => {
setIsValid(!Object.values(errors).reduce((acc, cur) => acc || cur !== undefined, false))
}, [errors])
const _set = (key: keyof T, value: any) => { const _set = (key: keyof T, value: any) => {
setForm({ setForm({
@ -35,34 +48,48 @@ export const useForm = <T, U extends { [key in keyof T]: useFormValidator }, E e
}) })
} }
const _validateAll = async (value: any, validator: useFormValidatorMethod): Promise<boolean> => { const _validateAll = async (value: any, object: useFormValidator): Promise<boolean> => {
if (validator.constructor.name === 'Function' || validator.constructor.name === 'AsyncFunction') const validator = isFormValidatorObject(object) ? object.validator : object
if (validator.constructor.name === 'Function')
return (validator as useFormValidatorFunction)(value) return (validator as useFormValidatorFunction)(value)
else if (validator.constructor.name === 'AsyncFunction')
return await (validator as useFormValidatorFunction)(value)
else if (validator.constructor.name === 'RegExp') else if (validator.constructor.name === 'RegExp')
return (validator as RegExp).test(value) return (validator as RegExp).test(value)
else return false else return false
} }
const _getValidatorMessage = (key: keyof T): string => {
// @ts-ignore
if (validators[key] && validators[key].message) return validators[key].message
else return `Error in: ${key}`
}
const _validate = (key: keyof T, value: any) => { const _validate = (key: keyof T, value: any) => {
const validator: useFormValidator | undefined = validators[key] const validator: useFormValidatorParameter | undefined = validators[key]
if (!validator) return if (!validator) return
// @ts-ignore if (Array.isArray(validator)) {
_validateAll(value, validator.constructor.name === 'Object' ? (validator as useFormValidatorObject).validator : validator) Promise.all(validator.map(v => _validateAll(value, v)))
.then((valid: boolean) => { .then(result => {
setErrors({ const index = result.indexOf(false)
...errors, setErrors({
[key]: valid ...errors,
? undefined [key]: index === -1
: _getValidatorMessage(key), ? undefined
: isFormValidatorObject(validator[index]) && (validator[index] as useFormValidatorObject).message
? (validator[index] as useFormValidatorObject).message
: defaultErrorMessage(key)
})
}) })
}) } else {
_validateAll(value, validator)
.then(valid => {
setErrors({
...errors,
[key]: valid
? undefined
: isFormValidatorObject(validator) && validator.message
? validator.message
: defaultErrorMessage(key)
})
})
}
} }
const update = (key: keyof T, extractor = options.extractor) => (value: any) => { const update = (key: keyof T, extractor = options.extractor) => (value: any) => {
@ -76,5 +103,5 @@ export const useForm = <T, U extends { [key in keyof T]: useFormValidator }, E e
[opts.setter || options.setter || 'value']: form[key] as any, [opts.setter || options.setter || 'value']: form[key] as any,
}) })
return { form, update, auto, errors } return { form, update, auto, errors, isValid }
} }

View File

@ -10,13 +10,19 @@ const TextError: React.FC<{ error?: string }> = ({ error }) => !error
const Index: React.FC = () => { const Index: React.FC = () => {
const { auto, form, errors } = useForm({ const { auto, form, errors, isValid } = useForm({
username: '', username: '',
password: '', password: '',
type: 'formhero', type: 'formhero',
awesome: true, awesome: true,
}, { }, {
username: /^test/, username: [
/^test/,
{
validator: async () => { return true },
message: 'Digits please',
}
],
password: { password: {
validator: /^.{3,}$/, validator: /^.{3,}$/,
message: 'To short', message: 'To short',
@ -26,7 +32,7 @@ const Index: React.FC = () => {
const _submit = (e: React.FormEvent) => { const _submit = (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
console.log(form, errors) console.log(form, errors, isValid)
} }
return ( return (