skip to Main Content

I have a login component that contains two inputs and button, which is disabled and enabled based on the error state and the fields. Both fields must be filled for the button to be enabled

For the first part the code works fine, the email field works the way I expect. The problem begins when I type in the password. Based on the condition I have in validateFields, I check if the password field value length is less than 8 chars and if so the button is still disabled else the button will be enabled. Once I reach 8 chars or more and remove the chars back to less the 8 chars the button remains enabled which is not the behaviour I expect. What could cause this behaviour?

Login component:

import React, { useContext } from "react";
import Button from "@mui/material/Button";
import { Link } from "react-router-dom";
import Container from "@mui/material/Container";
import TextFieldComponent from "./textField/textField.component";
import InputComponent from "./input/input.component";
import AuthContext from "../../context/auth.context";


const Login = () => {
  // styles Objects for Ui
  const containerStyles = {
    display: "flex",
    justifyContent: "space-between",
    padding: "0px",
  };

  const { toggleButtonDisabled, validation, setFormData,formData } = useContext(AuthContext);
  
   const checkSubmitButton = () => {
  
    if (formData.emailIsValid  && formData.passwordIsValid ) {
      toggleButtonDisabled(false);
    } else {
      toggleButtonDisabled(true);
    }
  };

  const changeHandler = (event) => {
    const { name, value } = event.target;
    setFormData(name, value)
    validation(name, value);
    checkSubmitButton();
  };


  return (
    <>
      <form action="">
        <TextFieldComponent
          name="email"
          label="Email"
          error={formData.fieldType === 'email' ? formData.error : ''}
          changeHandler={changeHandler}
        />

        <InputComponent
          name="password"
          label="Password"
          error={formData.fieldType === 'password' ? formData.error : ''}
          changeHandler={changeHandler}
        />
        <Container style={containerStyles} maxWidth="100%">
          <Link style={{ marginTop: "3px" }} component="button">
            Forgot Password ?
          </Link>
          <Button disabled={formData.submitDisabled} variant="contained">
            Sign In
          </Button>
        </Container>
      </form>

      <Container style={{ textAlign: "center", marginTop: "35px" }}>
        <span style={{ width: "100%", textAlign: "center", padding: "5px" }}>
          Don't have an account?
        </span>
        <Link to={"/signup"}>Sign Up</Link>
      </Container>
    </>
  );
};

export default Login;

the helper fucntion

export const validateFields = (fieldName, fieldValue,setForm) => {
    if (fieldName === "email") {
      const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}$/;
      const isValid = emailRegex.test(fieldValue);
      if (!isValid && fieldValue !== "") {
        setForm((prevFromData) => {
          return {
            ...prevFromData,
            error: true,
            errorMessage: "Email most be in a valid formant",
            emailIsValid: false,
            fieldType: "email",
          };
        });
      } else {
        setForm((prevFromData) => {
          return {
            ...prevFromData,
            error: false,
            errorMessage: "",
            emailIsValid: true,
            fieldType: "email",
          };
        });
      }
    }

    if (fieldName === "password") {
      console.log(fieldValue.length);
      if (fieldValue.length <7 ) {
        console.log('the value is less than 7');
        setForm((prevFromData) => {
          return {
            ...prevFromData,
            error: true,
            passwordIsValid: false,
            submitDisabled: true,
            fieldType: "password",
          };
        });
      } else{
        setForm((prevFromData) => {
          return {
            ...prevFromData,
            error: false,
            passwordIsValid: true,
            submitDisabled: false,
            fieldType: "password",
          };
        });
      }
    }
  };

react context

import React, { createContext,  useState } from "react";
import { validateFields } from "../helper/helpers";

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {

  const [formData, setForm] = useState({
    email: "",
    password: "",
    error: false,
    errorMessage: "",
    emailIsValid: false,
    passwordIsValid: false,
    submitDisabled: true,
    fieldType: "",

  });


  const setFormData = (name, value) => {
    setForm((prevData) => {
      return {
        ...prevData,
        [name]: value,
      };
    });
  }

  const validation = (fieldName, fieldValue)=>{
    validateFields(fieldName, fieldValue,setForm)
  }


  const toggleButtonDisabled = ( disabledStatus) => {

    setForm((prevFormData) => {
      return {
        ...prevFormData,
        submitDisabled: disabledStatus,
      };
    });

  }
  
  return (
    <AuthContext.Provider value={{ formData, setFormData, toggleButtonDisabled, validation }} >
      {children}
    </AuthContext.Provider>
  )



}

export default AuthContext;

2

Answers


  1. I think the problem is in your auth.context file:

    const validation = (fieldName, fieldValue)=>{
        validateFields(fieldName, fieldValue,setForm)
    }
    

    You defined setFormData before in that scope (which uses setForm). So the definition has to be probably:

    const validation = (fieldName, fieldValue)=>{
        validateFields(fieldName, fieldValue, setFormData)
    }
    

    I didn’t test it, so let me know if that solves your problem.

    Additionaly be aware of your check of the length of fieldValue.length. You’re currently checking if fieldValue.length < 7 as DustInComp already commented. If the minimum length is 8, it has to be either fieldValue.length < 8 or fieldValue.length <= 7.

    Login or Signup to reply.
  2. Found the problem. React State updates are asynchronous. So when your password length becomes less than 7, button is not updated (rendered) even though the state is updated. You can verify this by reducing the password length to 6 and then just update the email (keep it a valid email). Even though the email was already valid, the button would be disabled.

    To solve this you can use the useEffect hook, which would render the component whenever the dependency provided to it would update. here is the code you can use in your login.js file

    const Login = () => {
    // styles Objects for Ui
      const containerStyles = {
        display: "flex",
        justifyContent: "space-between",
        padding: "0px",
      };
    
      const { toggleButtonDisabled, validation, setFormData,formData } = useContext(AuthContext);
      
    
    useEffect(()=>{
        if (formData.emailIsValid  && formData.passwordIsValid ) {
          toggleButtonDisabled(false);
        } else {
          toggleButtonDisabled(true);
        }
      }, [formData.emailIsValid, formData.passwordIsValid]); //Re-render the component whenever these two fields are changed
    
      useEffect(()=>{
        if (formData.emailIsValid  && formData.passwordIsValid ) {
          toggleButtonDisabled(false);
        } else {
          toggleButtonDisabled(true);
        }
      }, []) //Update component when it mounts to the DOM
    //your code here .....
    

    and remove the checkSubmitButton method.

    PS : As pointed out in comments the correct condition for checking length should be fieldValue.length < =7

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search