Skip to content

Instantly share code, notes, and snippets.

@gchumillas
Last active March 28, 2021 12:15
Show Gist options
  • Save gchumillas/58baf61613e5f7deb243eb35c825e40a to your computer and use it in GitHub Desktop.
Save gchumillas/58baf61613e5f7deb243eb35c825e40a to your computer and use it in GitHub Desktop.
import React from 'react'
import _ from 'lodash'
export type Validator = (val: any) => string | true
const useValidator = ({
defaultValidator = () => true,
validators
}: {
defaultValidator?: Validator
validators?:
| { [name: string]: Validator[] }
| ((defaultValidator: Validator) => { [name: string]: Validator[] })
}) => {
const [texts, setTexts] = React.useState<{ [name: string]: string | true }>({})
return {
test: (values: { [name: string]: any }, options?: { debug: boolean }) => {
const texts = _.mapValues(values, (value, name) => {
const v = _.isFunction(validators) ? validators(defaultValidator) : validators
const fieldValidators = v?.[name] || [defaultValidator]
for (const validator of fieldValidators) {
const result = validator(value)
if (typeof result == 'string') {
options?.debug && console.info(`validator: ${name} is not valid`)
return result
}
}
return true
})
setTexts(texts)
return _.every(texts, x => _.isEmpty(x))
},
text: (name: string) => {
const text = texts[name]
return typeof text == 'string' ? text : undefined
}
}
}
export default useValidator
import React from 'react'
import { Button, TextField } from '@material-ui/core'
import useValidator from 'src/lib/validator'
const CustomTextField = ({ errorText, ...rest }) => {
return <TextField error={!!errorText} helperText={errorText} {...rest} />
}
const TestPage = () => {
const [message, setMessage] = React.useState('')
const [item, setItem] = React.useState({
username: '',
password: '',
rePassword: '',
gender: ''
})
const validator = useValidator({
// Default validator (applied when no validator is specified)
defaultValidator: val => !!val || 'Required field',
// Custom validators
validators: defaultValidator => ({
// rePassword is required (defaultValidator) and should be equal to password
rePassword: [defaultValidator, val => val == item.password || 'Passwords do not match'],
// Gender is not required, but when specified it must be one of the following values:
gender: [val => !val || ['male', 'female', 'butterfly'].includes(val) || 'Unknown gender']
})
})
const onFieldChange = name => event => {
setItem(item => ({ ...item, [name]: event.target.value }))
}
const onSubmit = () => {
if (!validator.test(item)) {
setMessage('No no no...')
return
}
setMessage('Hooray! you got it!')
}
return (
<>
<CustomTextField
required
label="Username"
value={item.username}
onChange={onFieldChange('username')}
errorText={validator.text('username')}
/>
<CustomTextField
required
type="password"
label="Password"
value={item.password}
onChange={onFieldChange('password')}
errorText={validator.text('password')}
/>
<CustomTextField
required
type="password"
label="Repeat password"
value={item.rePassword}
onChange={onFieldChange('rePassword')}
errorText={validator.text('rePassword')}
/>
<CustomTextField
label="Gender"
value={item.gender}
onChange={onFieldChange('gender')}
errorText={validator.text('gender')}
/>
<Button onClick={onSubmit}>Submit</Button>
<p>{message}</p>
</>
)
}
export default TestPage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment