1

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>
6
  • this.setState ? Commented Sep 17, 2020 at 7:26
  • 1
    You've disabled={!validateForm} on the submit button, validateForm isn't invoked. I also don't understand why you've mixed together a submit and reset button. Commented Sep 17, 2020 at 7:30
  • for disabled: the idea is to disable the reset button if the form fields are invalid. for onclick on reset: that's a mistake. I forget to remove it. I intend to go with onSubmit Commented Sep 17, 2020 at 7:40
  • @SakoBu I'm sorry, I do not follow. kindly elaborate Commented Sep 17, 2020 at 7:41
  • 2
    ResetPassword is a functional component, so calling this.setState is completely invalid. When are you trying to validate the form fields? On submit? Commented Sep 17, 2020 at 7:43

1 Answer 1

1

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;
Sign up to request clarification or add additional context in comments.

10 Comments

for some reason, form-actions is not rendering. I've attached an image of DOM imgur.com/a/DQoW7Z7
@NaumanTanwir Odd, there should at least be a Col and a Button in there, right? Is this in your stackblitz, or dev machine on localhost (or other)?
@NaumanTanwir I didn't change anything there in my solution, but in your stackblitz handleReset is the onClick handler for the reset button and it clears the error(s) by calling setError(null);. What error would/could be displayed when the form is reset?
@NaumanTanwir Basically my opinion, but that is a terrible idea. Submit and reset are intrinsically very different actions. Are you simply wanting the form to reset when submitted? If a user partially enters data and clicks that button, do you reset the form or submit it? See how confusing that is? From an accessibility standpoint alone that is a logistical nightmare.
@NaumanTanwir Previously you said you wanted validation to occur when the form was submitted, but it sounds more like you want form validation on blur (or change) of the fields. It's late and I must get going, but I can take a look at your updated stackblitz later.
|

Your Answer

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