henrydoe
henrydoe

Reputation: 397

handleSubmit and values not recognized in Formik form

I'm trying to create a login form using formik. I'm confused as to how to fire handleSubmit function to call the login api for a user to login. I kept calling handleSubmit inside onSubmit but it doesn't recognize the values and handleSubmit inside the onSubmit method in line 10 and 11 in my codesandbox in the ValidatedLoginForm.js file. Where do I exactly call the handleSubmit and let the user log in to my website?

my codesandbox

my code looks something like this:

import React, { useState } from "react";
import { Formik } from "formik";
import TextField from "@material-ui/core/TextField";
import * as Yup from "yup";

const ValidatedLoginForm = props => (
  <Formik
    initialValues={{ email: "", password: "" }}
    onSubmit={values => {
      const handleSubmit = async event => {
        event.preventDefault();

        var body = {
          password: password,
          email: email
        };
        console.log(body);
        const options = {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json"
          },
          body: JSON.stringify(body)
        };
        const url = "/api/authenticate";
        try {
          const response = await fetch(url, options);
          const text = await response.text();

          if (text === "redirect") {
            props.history.push(`/editor`);
          } else if (text === "verifyemail") {
            props.history.push(`/verifyOtp/${this.state.email}`);
          } else {
            console.log("login failed");
            window.alert("login failed");
          }
        } catch (error) {
          console.error(error);
        }
      };
    }}
    //********Using Yup for validation********/

    validationSchema={Yup.object().shape({
      email: Yup.string()
        .email()
        .required("Required"),
      password: Yup.string()
        .required("No password provided.")
        .min(8, "Password is too short - should be 8 chars minimum.")
        .matches(/(?=.*[0-9])/, "Password must contain a number.")
    })}
  >
    {props => {
      const {
        values,
        touched,
        errors,
        isSubmitting,
        handleChange,
        handleBlur,
        handleSubmit
      } = props;
      return (
        <>
          <form onSubmit={handleSubmit} noValidate>
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="email"
              value={values.email}
              label="Email Address"
              name="email"
              autoComplete="email"
              autoFocus
              onChange={handleChange}
              onBlur={handleBlur}
              className={errors.email && touched.email && "error"}
            />
            {errors.email && touched.email && (
              <div className="input-feedback">{errors.email}</div>
            )}
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              name="password"
              value={values.password}
              label="Password"
              type="password"
              id="password"
              onBlur={handleBlur}
              autoComplete="current-password"
              className={errors.password && touched.password && "error"}
              onChange={handleChange}
            />

            {errors.password && touched.password && (
              <div className="input-feedback">{errors.password}</div>
            )}
            <button type="submit" disabled={isSubmitting}>
              Login
            </button>
          </form>
        </>
      );
    }}
  </Formik>
);

export default ValidatedLoginForm;

Upvotes: 5

Views: 15264

Answers (1)

Jap Mul
Jap Mul

Reputation: 18769

You're currently creating a new function in your onSubmit code that never gets called. The function values => { ... } is called when the form is submitted, but in that function you create handleSubmit and never call it.

If you move the creation of handleSubmit a bit up it all gets easier to read. This will become something like

import React, { useState } from "react";
import { Formik } from "formik";
import TextField from "@material-ui/core/TextField";
import * as EmailValidator from "email-validator";
import * as Yup from "yup";

const ValidatedLoginForm = props => {
  // The function that handles the logic when submitting the form
  const handleSubmit = async values => {
    // This function received the values from the form
    // The line below extract the two fields from the values object.
    const { email, password } = values;
    var body = {
      password: password,
      email: email
    };
    console.log(body);
    const options = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json"
      },
      body: JSON.stringify(body)
    };
    const url = "/api/authenticate";
    try {
      const response = await fetch(url, options);
      const text = await response.text();

      if (text === "redirect") {
        props.history.push(`/editor`);
      } else if (text === "verifyemail") {
        props.history.push(`/verifyOtp/${this.state.email}`);
      } else {
        console.log("login failed");
        window.alert("login failed");
      }
    } catch (error) {
      console.error(error);
    }
  };

  // Returning the part that should be rendered
  // Just set handleSubmit as the handler for the onSubmit call.
  return (
    <Formik
      initialValues={{ email: "", password: "" }}
      onSubmit={handleSubmit}
      //********Using Yup for validation********/

      validationSchema={Yup.object().shape({
        email: Yup.string()
          .email()
          .required("Required"),
        password: Yup.string()
          .required("No password provided.")
          .min(8, "Password is too short - should be 8 chars minimum.")
          .matches(/(?=.*[0-9])/, "Password must contain a number.")
      })}
    >
      {props => {
        const {
          values,
          touched,
          errors,
          isSubmitting,
          handleChange,
          handleBlur,
          handleSubmit
        } = props;
        return (
          <>
            <form onSubmit={handleSubmit} noValidate>
              <TextField
                variant="outlined"
                margin="normal"
                required
                fullWidth
                id="email"
                value={values.email}
                label="Email Address"
                name="email"
                autoComplete="email"
                autoFocus
                onChange={handleChange}
                onBlur={handleBlur}
                className={errors.email && touched.email && "error"}
              />
              {errors.email && touched.email && (
                <div className="input-feedback">{errors.email}</div>
              )}
              <TextField
                variant="outlined"
                margin="normal"
                required
                fullWidth
                name="password"
                value={values.password}
                label="Password"
                type="password"
                id="password"
                onBlur={handleBlur}
                autoComplete="current-password"
                className={errors.password && touched.password && "error"}
                onChange={handleChange}
              />

              {errors.password && touched.password && (
                <div className="input-feedback">{errors.password}</div>
              )}
              <button type="submit" disabled={isSubmitting}>
                Login
              </button>
            </form>
          </>
        );
      }}
    </Formik>
  );
};

export default ValidatedLoginForm;

I would also move the validationSchema out of your component. Makes it easier to read/understand and it doesn't have to be recreated every time.

Upvotes: 6

Related Questions