Upon clicking the SubmitBtn in the LoginForm component for the first time, the form fails to submit, and nothing appears to happen. The onInvalid function returns an empty object ({}). However, on the second attempt, the onInvalid function provides the following feedback:
{
username: { message: 'Required', type: 'invalid_type', ref: undefined },
confirmPassword: { message: 'Required', type: 'invalid_type', ref: undefined }
}
Despite this detailed validation error, the form remains unsubmitted, and no observable changes occur.
LogInForm :
import { FormControl } from '@chakra-ui/react'
import SubmitBtn from '../SubmitBtn'
import InputComponent from '../InputComponent';
import LoginService from '../../../services/loginService';
const LoginForm = () => {
const { handleSubmit, onsubmit, register, isPending, errors, onInvalid } = LoginService()
return (
<form onSubmit={handleSubmit(onsubmit, onInvalid)}>
<FormControl isRequired mb='4'>
<InputComponent label='Email Address' register={register} errorsType={errors} errorsMessage={errors.email?.message} placeholder='[email protected]' type='email' name='email' helper='please make sure to enter a valid email address' />
</FormControl>
<FormControl isRequired mb='4'>
<InputComponent label='Password' register={register} errorsType={errors} errorsMessage={errors.password?.message} placeholder='enter your password: ******' type='password' name='password' helper='please make sure to enter the correct password' />
</FormControl>
<SubmitBtn loading={isPending} textloading='logging in' >Login</SubmitBtn>
</form>
)
}
export default LoginForm
InputComponent
import { FormHelperText, Input, FormLabel } from '@chakra-ui/react'
import type { FieldErrors, UseFormRegister } from 'react-hook-form';
import { FormType } from '../../types/types';
type InputComponentPropsTypes = {
register: UseFormRegister<FormType>,
errorsType?: FieldErrors<FormType> | undefined,
errorsMessage: string | undefined,
type: React.HTMLInputTypeAttribute,
name: keyof FormType
label: string
placeholder: string,
helper: string
}
const InputComponent = ({ label, errorsType, errorsMessage, register, name, type, placeholder, helper }: InputComponentPropsTypes) => {
return (
<>
<FormLabel>{label}</FormLabel>
<Input {...register(name)} type={type} placeholder={placeholder} focusBorderColor='teal.500' />
{errorsType && errorsType[name] ?
<FormHelperText color='red'>{errorsMessage}</FormHelperText>
:
<FormHelperText>{helper}</FormHelperText>
}
</>
)
}
export default InputComponent
LoginService
import { useToast } from '@chakra-ui/react'
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form'
import axios from 'axios'
import { useMutation } from '@tanstack/react-query'
import { useNavigate } from 'react-router-dom'
import { z } from 'zod'
import { useStore } from '../utils/store';
import { FormSchema, FormType } from '../types/types';
import { API_URL } from '../constants';
const LoginService = () => {
const setToken = useStore(store => store.setToken)
const toast = useToast()
const navigation = useNavigate()
const { reset, register, handleSubmit, formState: { errors } } = useForm<FormType>({ resolver: zodResolver(FormSchema) })
const { mutateAsync: loginUser, isPending } = useMutation({
mutationKey: ['loginUser'],
mutationFn: async (user: FormType) => {
await axios.post(`${API_URL}auth/login`, user).then(res => {
setToken(res.data.token)
localStorage.setItem('token', res.data.token)
})
reset()
}
})
const onInvalid = () => console.log(errors)
const onsubmit = (user: FormType) => {
console.log('clicked')
loginUser(user, {
onSuccess: () => {
toast({
title: 'successfully logged in',
position: 'top',
status: 'success',
isClosable: true,
})
navigation('/home')
},
onError: (error) => {
if (error instanceof z.ZodError) {
toast({
title: `${error.issues[0].message}`,
position: 'top',
status: 'error',
isClosable: true,
})
} else toast({
title: 'something went wrong',
position: 'top',
status: 'error',
isClosable: true,
})
}
})
}
return {
onsubmit,
onInvalid,
register,
handleSubmit,
isPending,
errors
}
}
export default LoginService
Types
import { z } from "zod"
export type AuthType = 'register' | 'login'
export const FormSchema = z.object({
username: z.string().nullable(),
email: z.string().email(),
password: z.string().min(8, { message: 'Must be 8 or more characters long' }),
confirmPassword: z.string().nullable()
}).refine(data => data.password === data.confirmPassword, {
message: 'confirmed password must match the password',
path: ['confirmPassword']
})
export type FormType = z.infer<typeof FormSchema>;
The form was functioning correctly initially. However, after splitting it into multiple components, the issue arose.
2
Answers
The solution I found is to add a new schema for the login.
And picking only the email and password from FormType in loginService.
It should work with the optional() method, but I don't know why it didn't work. Can anyone explain why?
onSubmit
function: You are using onSubmit incorrectly in your LoginForm component. You need to pass a single function to the onSubmit attribute of the element, but you’re passing two functions (handleSubmit and onsubmit combined).Form Submission: Ensure that your form submission is properly handled. Check if the handleSubmit function is wrapping your onsubmit function correctly to manage form submission and validation.
To rectify these issues, you can make the following adjustments:
In your LoginForm component, modify the onSubmit attribute of the element:
And in your LoginService, make sure to return the handleSubmit function and the necessary handlers:
This way, the onSubmitHandler will correctly invoke both the handleSubmit and onsubmit functions in sequence when the form is submitted.