This repository has been archived on 2024-10-29. You can view files and clone it, but cannot push or open issues or pull requests.
website/hooks/useForm.ts

119 lines
3.4 KiB
TypeScript
Raw Normal View History

2021-10-24 05:19:39 +02:00
import { useMemo, useState } from 'react'
import { FormDataObject, HandleForm } from 'react-component-form'
import useTranslation from 'next-translate/useTranslation'
import { FormState, useFormState } from 'hooks/useFormState'
import {
GetErrorMessages,
useFastestValidator,
ValidatorSchema
} from 'hooks/useFastestValidator'
import { ValidationError } from 'fastest-validator'
export interface ErrorResponse {
field?: string
message: string
}
export interface UseFormOptions {
validatorSchema: ValidatorSchema
}
export type HandleSubmit = (callback: HandleSubmitCallback) => HandleForm
export type HandleSubmitCallback = (
formData: FormDataObject
) => Promise<string | null>
export interface UseFormResult {
message: string | undefined
formState: FormState
getErrorMessages: GetErrorMessages
handleChange: HandleForm
handleSubmit: HandleSubmit
}
export const useForm = (options: UseFormOptions): UseFormResult => {
const { validatorSchema } = options
const { lang, t } = useTranslation()
const errorsMessages = useMemo(() => {
return {
stringMin: t('errors:stringMin'),
stringEmpty: t('errors:required'),
emailEmpty: t('errors:required'),
required: t('errors:required'),
email: t('errors:email'),
alreadyUsed: t('errors:alreadyUsed'),
invalid: t('errors:invalid')
}
}, [lang])
const [formState, setFormState] = useFormState()
const {
validate,
getErrorMessages,
addValidationErrors
} = useFastestValidator(validatorSchema, {
messages: errorsMessages
})
const [message, setMessage] = useState<string | undefined>(undefined)
const handleChange: HandleForm = (formData) => {
if (formState !== 'error') {
setMessage(undefined)
}
const isValid = validate(formData)
setFormState(!isValid ? 'error' : 'idle')
}
const handleSubmit = (callback: HandleSubmitCallback): HandleForm => {
return async (formData, formElement) => {
const isValid = validate(formData)
if (isValid) {
setFormState('loading')
try {
const successMessage = await callback(formData)
if (successMessage != null) {
setMessage(successMessage)
setFormState('success')
formElement.reset()
}
} catch (error) {
if (error.response == null) {
setFormState('error')
setMessage(t('errors:server-error'))
return
}
const errors = error.response.data.errors as ErrorResponse[]
const validationErrors: ValidationError[] = []
for (const error of errors) {
if (error.field != null) {
if (error.message.endsWith('already used')) {
validationErrors.push({
type: 'alreadyUsed',
field: error.field
})
} else {
validationErrors.push({
type: 'invalid',
field: error.field
})
}
} else {
setFormState('error')
setMessage(error.message)
break
}
}
addValidationErrors(validationErrors)
setFormState('error')
}
} else {
setMessage(undefined)
setFormState('error')
}
}
}
return { message, formState, getErrorMessages, handleChange, handleSubmit }
}