In my project, I am using antd Form in the login screen. If the user hasn't provided email or if the email is invalid, I am handling that using the rules attribute that antd's Form.Item supports.
Here is what I wanted to achieve:
I also wanted to handle another error which is 'No account associated with email'. After the user click on the login button, there is a back-end API call. When the backend returns this error in the response, then UI should show this error message under the email input.
To achieve this, I am storing this error message in a state variable 'emailError' and I'm using this in the 'rules' attribute of the Form.Item as follows:
function Login() {
const [loginForm] = Form.useForm();
const [emailError, setEmailError] = useState({ enable: false, helpText: "" });
const emailErrorRef = useRef(emailError);
const onLoginFormSubmit = async (values) => {
try {
let response = await loginRequest(values);
console.log("Login -> onLoginFormSubmit -> response", response);
// ...some logic
} catch (err) {
/*
err.response.data = {
code: 'ERR_LOGIN_NO_ACCOUNT_WITH_EMAIL',
detail: 'No account associated with this email'
}
*/
let errCode = err.response.data.code;
let errMessage = err.response.data.detail;
// If the API fails with the below error, then update the state variable.
if (errCode === "ERR_LOGIN_NO_ACCOUNT_WITH_EMAIL") {
console.log(
"Login -> update emailError -> ERR_LOGIN_NO_ACCOUNT_WITH_EMAIL"
);
setEmailError({
enable: true,
helpText: errMessage
});
}
}
};
useEffect(() => {
console.log("Login -> updated emailError", emailError);
emailErrorRef.current = emailError;
}, [emailError]);
return (
<div
style={{
position: "fixed",
width: 400,
height: 200,
top: "calc(50% - 100px)",
left: "calc(50% - 200px)"
}}
>
<Form form={loginForm} onFinish={onLoginFormSubmit}>
<Form.Item
name="email"
rules={[
{
type: "email",
message: "Please provide a valid email!"
},
{ required: true, message: "Please provide your email!" },
() => ({
validator() {
console.log(
"Inside email validator! -> emailError",
emailError
);
if (emailError.enable) {
return Promise.reject(emailError.helpText);
}
return Promise.resolve();
}
})
]}
>
<Input autoFocus placeholder="Email" prefix={<EmailIcon />} />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true, message: "Please provide your password!" }]}
>
<Input.Password placeholder="Password" prefix={<PasswordIcon />} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Login
</Button>
</Form.Item>
</Form>
</div>
);
}
From what I can understand based on the logs, I think that the Form is not being re-rendered when I am updating the state variable 'emailError' after receiving the response from async API call.
UPDATE: I am also logging the state variable 'emailError' in the HTML. The change is state variable is reflected in the UI. So, I think that the re-rendering is working fine. But, during the re-render, the rules are not being validated..
I have tried to use 'emailErrorRef.current' instead of 'emailError' in the validator() but this didn't work.
What am I missing here? Please help fix this!
Here is a codesandbox which replicates this: