6

See Update Below

I have a Login Form Component that I built using Formik that syncs with Firebase Authentication. I have set it up so that I can display errors from Firebase using the setFieldError prop. Here is the relevant sections of the code:

export const LoginForm = () => {
  async function authenticateUser(values, setFieldError) {
    const { email, password } = values

    try {
      await firebase.login(email, password)
      navigate('/', { replace: true })
    } catch (error) {
      console.log('Authentication Error: ', error)
      await setFieldError('firebaseErrorMessage', error.message)
    }
  }

  return (
    <>
      <h1>Form</h1>
      <Formik
        render={props => <RenderForm {...props} />}
        initialValues={initialValues}
        validationSchema={validationSchema}
        onSubmit={async (
          values,
          { setFieldError, setSubmitting, resetForm }
        ) => {
          setSubmitting(true)
          authenticateUser(values, setFieldError)
          setSubmitting(false)
          resetForm()
        }}
      />
    </>
  )
}

const RenderForm = ({ errors, isSubmitting, isValid }) => (
  <Form>
    <h3>Sign Up</h3>
    <Email name="email" />
    <Password name="password" />
    <Button disabled={!isValid || isSubmitting} type="submit">
      Submit
    </Button>
    {errors.firebaseErrorMessage && <p>{errors.firebaseErrorMessage}</p>}
  </Form>
)

Now, this works just fine. However, if I try to display the error message using Formik's ErrorMessage component, then the message does not show up.

In other words, this works:

{errors.firebaseErrorMessage && <p>{errors.firebaseErrorMessage}</p>}

This does not work:

<ErrorMessage name="firebaseErrorMessage" />

Any idea why this doesn't work and how to get it to work?

Thanks.

UPDATE

Here are my initial values:

const initialValues = {
  email: '',
  password: '',
}
1
  • Does it make a difference if you await authenticateUser(values, setFieldError); in your onSubmit? Also, can you include your initialValues object? Commented Sep 22, 2019 at 14:41

1 Answer 1

7

I don't think that you should use Formik's error for your Firebase error. Formik's errors are meant for validating form inputs.

To store and reference an API error, you can use Formik's status object. Handling API errors is the example he gives for status.

I think the issue is that, in Formik, name is meant to refer to the name of an input. Instead, you're imperatively adding a new name property to the errors object using setFieldError, but firebaseErrorMessage isn't a field in your form. (Share your initialValues object to verify this.)

One annoying part of this is that there is probably some styling associated with <ErrorMessage> that you can't leverage directly. But, in my opinion, it's probably more important to have your system structured correctly, and then you can mimic styles as-needed.

Here's my code suggestion:

const RenderForm = ({ isSubmitting, isValid, status }) => (
  <Form>
    <h3>Sign Up</h3>
    <Email name="email" />
    <Password name="password" />
    <Button disabled={!isValid || isSubmitting} type="submit">
      Submit
    </Button>
    {status.firebaseErrorMessage && <p>{status.firebaseErrorMessage}</p>}
  </Form>
);

export const LoginForm = () => {
  async function authenticateUser(values, setStatus, setSubmitting) {
    const { email, password } = values;

    setSubmitting(true);

    try {
      await firebase.login(email, password);
      navigate("/", { replace: true });
    } catch (error) {
      console.log("Authentication Error: ", error);
      setStatus({
        firebaseErrorMessage: error.message
      });
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <>
      <h1>Form</h1>
      <Formik
        render={props => <RenderForm {...props} />}
        initialValues={initialValues}
        validationSchema={validationSchema}
        onSubmit={async (values, { setStatus, setSubmitting, resetForm }) => {
          await authenticateUser(values, setStatus, setSubmitting);
          resetForm();
        }}
      />
    </>
  );
};
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the explanation of why the ErrorMessage component won't work. With that said, I'm not sure I understand the advantage of of useState over setFieldError. Would you mind explaining why one is better than the other? Thanks.
Formik's errors object holds errors which correspond to the values of the form. It's meant to say, "Your email field doesn't contain valid email syntax", or "Password can't contain symbols", that sort of thing. Formik is used to validate form data. Each form input might need to be validated differently. Here, your concern is the API error. It's not directly related to the structure of the data input. I think it makes sense to create your own errors object for concerns unrelated to the form inputs' validity. It's an API error, not a form error.
I just remembered Formik's status object. Try that instead of state I think.
Thanks three times over -- for the answer, the explanation and the link to status.
I'm using Redux to dispatch the API request so couldn't get my head around how I would set the status at the right point with that.. ended up using useEffect and formik.org/docs/api/useFormikContext#example
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.