I'm trying to make a custom reusable useForm hook with inputs validation. I want form to be validated on Submit and clear validation when user typing something in the field. Actually the problem with validation. I do not understand how to implement it.
Hook accepts inital state for inputs which is an object with keys and values as empty strings:
{
"name": "",
"description": ""
}
Hooks also accept a callback function which is actually a submitHandler which fires on submit click but do nothing at the moment.
I have a submit function in useForm hook which expected to fire when a form is valid. To check the form validity I loop through inputs calling validator function on each input. If input is not valid I set an error for that field.
But its not clear what to do next. If I console.log(error) in submit function right after forEach loop after first submit button click its an empty object (on second button click it has errors but not for the first click).
May I ask for help tp deal with problem? How to make this validation work correctly?
Submit function in useForm hook:
const submit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
Object.entries(input).forEach(([field, value]) => {
validator(field, value)
})
console.log(error) // it's an empty object on first submit button click
callback();
};
Validator function:
const validator = (field: keyof Error, value: Input['key']) => {
switch (field) {
case 'name':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for name field',
}));
}
break;
case 'description':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for description field',
}));
}
break;
default:
break;
}
};
Full hook code:
import { useState } from 'react';
type Input = {
[key: string]: string;
};
type Error = {
[key: string]: string | undefined;
};
const useForm = (initValue: Input, callback: () => void) => {
const [input, setInput] = useState(initValue);
const [errors, setErrors] = useState<Error>({});
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
clearValidation(name);
setInput((prevState) => ({
...prevState,
[name]: value,
}));
};
const resetForm = () => {
setInput({});
};
const submit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
Object.entries(input).forEach(([field, value]) => {
validator(field, value);
});
callback();
};
const validator = (field: keyof Error, value: Input['key']) => {
switch (field) {
case 'name':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for name field',
}));
}
break;
case 'description':
if (value.length <= 2) {
setErrors((prev) => ({
...prev,
[field]: 'Min 2 characters for description field',
}));
}
break;
default:
break;
}
};
const clearValidation = (field: keyof Error) => {
if (field) {
setErrors((prevState) => ({
...prevState,
[field]: undefined,
}));
return;
}
setErrors({});
};
return { input, errors, setInput, handleChange, resetForm, submit };
};
export default useForm;