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
I think the problem is in your
auth.context
file:You defined
setFormData
before in that scope (which usessetForm
). So the definition has to be probably: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 iffieldValue.length < 7
as DustInComp already commented. If the minimum length is 8, it has to be eitherfieldValue.length < 8
orfieldValue.length <= 7
.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
and remove the
checkSubmitButton
method.PS : As pointed out in comments the correct condition for checking length should be
fieldValue.length < =7