user6488504
user6488504

Reputation:

react-hook useFormInput() form validation not working

Link to code sandbox https://stackblitz.com/edit/react-cdv6mx?devtoolsheight=33&file=src/ResetPassword.js

EXPLANATION: When the user enters incorrect details, the server sends 400: bad request error with a message which can be seen in the Response tab under the network's tab. The intention is to validate the form fields before submitting the form and display an error message(s) if incorrect fields are entered. Also, the intention is to disable to reset the button until the form fields match the criteria. So, that will prevent the user from accidentally submitting half-entered fields.

I have a functional component in which I have a simple form.

PROBLEM: I am calling validatForm() method on onChange. The method should first check that the newPassword and confirmPassword are the same and are as per the password rules(requirements) and if true, only then, send the data.

UPDATED CODE IS IN THE STACKBLITZ LINK ABOVE.

I am use useFormInput() like below

  const email = useFormInput("");
  const newPassword = useFormInput("");
  const confirmPassword = useFormInput("");

and I have written a useFormInput() method

const useFormInput = (initialValue) => {
  const [value, setValue] = useState(initialValue);

  const handleChange = (e) => {
    setValue(e.target.value);
  };
  return {
    value,
    onChange: handleChange,
  };
};

and a validateForm() method and I am passing it my <button/>

/*  Your password must contain at least one capital letter, one
      *number and one lowercase letter, and it must contain at least 8
      *characters*/


const validateForm = (event) => {
    let pass = event.target.value;
    let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/;
    let test = reg.test(pass);
    if (test) {
      this.setState({ value: event.target.value });
    } else {
      alert("password validation unsuccessful. Please try again.");
      return;
    }
  };

and in render()

<FormGroup row>
            <Label for="Email" sm={2}>
              Email
            </Label>
            <Col sm={4}>
              <Input
                type="email"
                name="email"
                id="Email"
                
                {...email}
              />
            </Col>
          </FormGroup>
          <FormGroup row>
            <Label for="newPassword" sm={2}>
              New Password
            </Label>
            <Col sm={4}>
              <Input
                type="password"
                name="password"
                id="newPassword"
                
                {...newPassword}
              />
            </Col>
          </FormGroup>
          <FormGroup row>
            <Label for="confirmPassword" sm={2}>
              Confirm Password
            </Label>
            <Col sm={4}>
              <Input
                type="password"
                name="confirmPassword"
                id="confirmPassword"
                
                {...confirmPassword}
              />
            </Col>
          </FormGroup>
          <div className="form-actions">
            {error && (
              <>
                <small style={{ color: "red" }}>{error}</small>
                <br />
              </>
            )}
            <br />

            <Col lg={{ offset: 2 }} sm={{ size: 1 }}>
              <Button
                className="mail-reset-btn"
                block
                type="submit"
                value={loading ? "Loading..." : "Login"}
                onClick={handleReset}
                disabled={!validateForm}
              >
                Reset
              </Button>
            </Col>
          </div>
        </Form>

Upvotes: 1

Views: 1443

Answers (1)

Drew Reese
Drew Reese

Reputation: 202618

Update your validateForm to destructure the password fields from the form onSubmit event. Create an errors object to track what field errors there are. If the errors object is empty then validation passes, otherwise set error state.

const validateForm = event => {
  event.preventDefault();

  const { confirmPassword, newPassword } = event.target;
  const errors = {};

  const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/;

  if (!regex.test(newPassword.value)) {
    errors.requirementUnmet = "New password does not meet requirements.";
  }

  if (newPassword.value !== confirmPassword.value) {
    errors.passwordMismatch = "Entered passwords do not match.";
  }

  if (!Object.keys(errors).length) {
    alert("password validation successful.");
    setError(null);
    return;
  }

  setError(errors);
};

Attach validateForm to the form's onSubmit handle.

<Form onSubmit={validateForm}>

Ensure you've a type="submit" button in the form.

<Button
  className="mail-submit-btn"
  block
  type="submit"
  value={loading ? "Loading..." : "Login"}
>
  Submit
</Button>

Updated stackblitz (though I've not an account so copying updated code below.)

Note: I simply JSON.stringify(error) the error for simplicity, but you'll want to render this more gracefully.

Entire ResetPassword.js

import React, { useState } from "react";
import {
  Button,
  Form,
  FormGroup,
  Input,
  Col,
  Label,
  Modal,
  ModalHeader,
  ModalBody,
  ModalFooter
} from "reactstrap";

const useFormInput = initialValue => {
  const [value, setValue] = useState(initialValue);

  const handleChange = e => {
    setValue(e.target.value);
  };
  return {
    value,
    onChange: handleChange
  };
};

const ResetPassword = props => {
  // form inputs
  const email = useFormInput("");
  const newPassword = useFormInput("");
  const confirmPassword = useFormInput("");
  // using hooks
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const [modal, setModal] = useState(false);

  const toggle = () => setModal(!modal);

  const validateForm = event => {
    event.preventDefault();

    const { confirmPassword, newPassword } = event.target;
    const errors = {};

    const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/;

    if (!regex.test(newPassword.value)) {
      errors.requirementUnmet = "New password does not meet requirements.";
    }

    if (newPassword.value !== confirmPassword.value) {
      errors.passwordMismatch = "Entered passwords do not match.";
    }

    if (!Object.keys(errors).length) {
      alert("password validation successful.");
      setError(null);
      return;
    }

    setError(errors);
  };

  const handleReset = e => {
    e.preventDefault();
    setError(null);
    setLoading(true);
    // some unrelated redux code
  };

  return (
    <div className="mail-reset" id="forgotPassword">
      <div className="mail-reset-content">
        <Form onSubmit={validateForm}>
          <h3 className="form-title">Enter Information</h3>

          <FormGroup row>
            <Label for="Email" sm={2}>
              Email
            </Label>
            <Col sm={4}>
              <Input
                type="email"
                name="email"
                id="Email"
                placeholder="Email"
                aria-label="email address"
                aria-describedby="email address"
                aria-invalid="false"
                {...email}
              />
            </Col>
          </FormGroup>
          <FormGroup row>
            <Label for="newPassword" sm={2}>
              New Password
            </Label>
            <Col sm={4}>
              <Input
                type="password"
                name="password"
                id="newPassword"
                placeholder="New Password"
                aria-label="new password"
                aria-describedby="new password"
                aria-invalid="false"
                {...newPassword}
              />
            </Col>
          </FormGroup>
          <FormGroup row>
            <Label for="confirmPassword" sm={2}>
              Confirm Password
            </Label>
            <Col sm={4}>
              <Input
                type="password"
                name="confirmPassword"
                id="confirmPassword"
                placeholder="Confirm Password"
                aria-label="new password"
                aria-describedby="new password"
                aria-invalid="false"
                {...confirmPassword}
              />
            </Col>
          </FormGroup>

          <div className="modal-wrapper">
            <Col sm={{ size: 4, offset: 2 }}>
              <Button onClick={toggle} className="passwordReqBtn">
                Password Requirements
              </Button>
            </Col>
            <Modal isOpen={modal} toggle={toggle} className="mail-reset-modal">
              <ModalHeader toggle={toggle}>Password Requirements</ModalHeader>
              <ModalBody>
                Your password must contain at least one capital letter, one
                number and one lowercase letter, and it must contain at least 8
                characters
              </ModalBody>
              <ModalFooter>
                <Button onClick={toggle} className="btn-modal pull-right">
                  OK
                </Button>{" "}
              </ModalFooter>
            </Modal>
          </div>

          <div className="form-actions">
            {error && (
              <>
                <small style={{ color: "red" }}>{JSON.stringify(error)}</small>
                <br />
              </>
            )}
            <br />

            <Col lg={{ offset: 2 }} sm={{ size: 1 }}>
              <Button
                className="mail-reset-btn"
                block
                type="submit"
                value={loading ? "Loading..." : "Login"}
              >
                Submit
              </Button>
              <Button
                className="mail-reset-btn"
                block
                type="reset"
                value={loading ? "Loading..." : "Login"}
                onClick={handleReset}
              >
                Reset
              </Button>
            </Col>
          </div>
        </Form>
      </div>
    </div>
  );
};

export default ResetPassword;

Upvotes: 1

Related Questions