Need help figuring out why I'm getting an error in a React Form

Viewed 84

I have a login form in React, using React Hook Form, that was working perfectly fine and out of the blue, I started getting errors. Uncaught TypeError: Cannot read properties of undefined (reading 'email'). I am really struggling to understand why I'm getting the error. Here is the code: For simplicity, I removed the styled divs at the bottom. Any help is much appreciated.

import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import styled from 'styled-components';
import GlobalStyle from '../globalStyles';
import BackgroundImage from '../images/blueSwoosh.png';
import MainImage from '../images/main-image.png';
import { loginUser, useAuthState, useAuthDispatch } from '../context';

function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const userSchema = yup
    .object()
    .shape({
      email: yup.string().email().required(),
      password: yup.string().required(),
    })
    .required();

  const { register, handleSubmit, errors } = useForm({
    resolver: yupResolver(userSchema),
  });

  const handleEmailChange = (e) => {
    setEmail(e.target.value);
  };

  const handlePasswordChange = (e) => {
    setPassword(e.target.value);
  };

  const dispatch = useAuthDispatch(); 
  const { loading, errorMessage } = useAuthState();

  const navigate = useNavigate();

  const handleFormSubmit = async (data, e) => {
    e.preventDefault();
    try {
      const response = await loginUser(dispatch, { email, password });
      if (!response) return;
      navigate('/user');
    } catch (error) {
      console.log(error);
    }
    setEmail('');
    setPassword('');
  };

  return (
    <Container>
      <LeftSideWrapper>
        <GlobalStyle />
        <Header>slackers</Header>
        {errorMessage ? <ErrorMsg>{errorMessage}</ErrorMsg> : null}
        <Underline />
        <Text>where those who slack go to chat</Text>
        <Form onSubmit={handleSubmit(handleFormSubmit)}>
          <Input
            {...register('email', { required: true })}
            placeholder="email"
            value={email}
            onChange={(e) => handleEmailChange(e)}
          />
          {errors.email?.message}
          <Input
            {...register('password', { required: true })}
            placeholder="password"
            value={password}
            onChange={(e) => {
              handlePasswordChange(e);
            }}
          />
          {errors.password?.message}
          <LoginBtn as="button" type="submit" disabled={loading}>
            Login
          </LoginBtn>
        </Form>
      </LeftSideWrapper>

      <RightSideWrapper>
        <ImageBox src={MainImage} alt="people conversing" />
      </RightSideWrapper>
    </Container>
  );
}


export default Login;

I have declared email in the useState and it was working fine before. I tried re-installing dependencies, setting a value for the email (in useState), clearing the cache, and obviously reviewing the code against a prior commit (where everything was working but before the authorization was added). I also tried commenting out anything related to the authorization but to no avail. I don't know what I'm missing and it's driving me crazy.

1 Answers

Two small but important fixes. Firstly, there's no errors field in the object you get back from useForm() - errors is a sub-field of the formState field, so you need to do:

const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: yupResolver(userSchema),
  });

Secondly, once you've got this errors object back from react-hook-form, it might be undefined - so your chaining expression errors.email?.message will fail.

Just add an extra bit of optional chaining to fix it, as per the react-hook-form docs:

  {errors?.email?.message}

  // and later on

  {errors?.password?.message}
Related