I use ReactBootstrap in styled-component , storage formData in Context
I want the Form.Control maintain its original style and start checking validate only after my first input.
When I onChange name and account , the feedback div can work correctly ,
But when I onChange password and confirmPassword , their feedback div rendering does not update with the onChange event.
Context is correct , but render is incorrect ,
I know setState means update in next render , but my confirmCheck is a state too ?
How can I fix this
FormContainer.jsx
import { useState } from "react";
import Form from "react-bootstrap/Form";
function FormContainer({ children, handleSubmitExtend }) {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
console.log("組件", form);
event.preventDefault();
event.stopPropagation();
setValidated(true);
handleSubmitExtend();
};
return (
<Form noValidate validated={validated} onSubmit={handleSubmit}>
{children}
</Form>
);
}
export default FormContainer;
FormInput.jsx
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Form from "react-bootstrap/Form";
import { useEffect, useState } from "react";
export default function FormInput({
label,
type,
value,
placeholder,
onChange,
invalidPrompt,
minlength,
maxlength,
confirmCheck = true,
}) {
const handleFormChange = (e) => {
const inputNode = e.target;
if (inputNode.validity.valid && confirmCheck) {
inputNode.classList.remove("is-invalid");
inputNode.classList.add("is-valid");
} else {
inputNode.classList.add("is-invalid");
inputNode.classList.remove("is-valid");
}
};
return (
<Row>
<Form.Group as={Col} md="12">
<Form.Label htmlFor={label}>{label}</Form.Label>
<Form.Control
className="mb-3 input-rows"
id={label}
required
type={type}
placeholder={placeholder}
defaultValue={value}
value={value}
onChange={(e) => {
onChange?.(e.target.value);
handleFormChange(e);
}}
minLength={minlength}
maxLength={maxlength}
/>
<Form.Control.Feedback>OK!</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">
{invalidPrompt}
</Form.Control.Feedback>
</Form.Group>
</Row>
);
}
RegisterPage.jsx
import styled from "styled-components";
import { useEffect, useState } from "react";
import { useAuth } from "../context/AuthContext";
import Swal from "sweetalert2";
import FormContainer from "../components/AuthForm/FormContainer";
import FormInput from "../components/AuthForm/FormInput";
export default function RegisterPageTest() {
const defaultForm = {
account: "",
password: "",
confirmPassword: "",
name: "",
};
const [form, setForm] = useState(defaultForm);
const [confirmCheck, setConfirmCheck] = useState(true);
const handleInputOnchange = (attr, inputValue) => {
setForm({
...form,
[attr]: inputValue,
});
setConfirmCheck(form.password === form.confirmPassword);
};
const { login, isLogin } = useAuth();
const handleSubmit = async () => {
const { name, account, password, confirmPassword } = form;
console.log(name, account);
if (
name.length < 2 ||
account.length < 4 ||
password.length < 4 ||
confirmPassword.length < 4
) {
Swal.fire({
title: "error",
text: "Error",
icon: "question",
});
return;
}
try {
const status = await login(form);
if (status === "success") {
Swal.fire({
title: "success!",
text: "success",
icon: "success",
});
} else {
Swal.fire({
title: "註冊失敗!",
button: "好",
icon: "success",
});
}
} catch (error) {
console.log(error);
}
};
return (
<AuthPage>
<AuthContainer>
<FormContainer handleSubmitExtend={handleSubmit}>
<AuthTitle>Sign up for free </AuthTitle>
<FormInput
key={form}
label="account"
type="text"
placeholder="account"
value={form.account}
onChange={(inputValue) =>
handleInputOnchange("account", inputValue)
}
invalidPrompt={"At least 4 or more characters"}
minlength={4}
maxlength={16}
></FormInput>
<FormInput
key={form}
label="password"
type="password"
placeholder="password..."
value={form.password}
onChange={(inputValue) =>
handleInputOnchange("password", inputValue)
}
invalidPrompt={"At least 4 or more characters"}
minlength={4}
maxlength={16}
confirmCheck={confirmCheck}
></FormInput>
<FormInput
key={form}
label="confirmPassword"
type="password"
placeholder="confirmPassword"
value={form.confirmPassword}
onChange={(inputValue) =>
handleInputOnchange("confirmPassword", inputValue)
}
invalidPrompt={
form.confirmPassword !== form.password
? "password doesn't match"
: "At least 4 or more characters"
}
confirmCheck={confirmCheck}
minlength={4}
maxlength={16}
></FormInput>
<FormInput
key={form}
label="用戶名稱"
type="text"
placeholder="username..."
value={form.name}
onChange={(inputValue) => handleInputOnchange("name", inputValue)}
invalidPrompt={"At least 1 or more characters"}
minlength={1}
maxlength={16}
></FormInput>
<AuthButton>註冊</AuthButton>
<AuthLink>
已經有帳號了? <a href="/login">登入</a>
</AuthLink>
</FormContainer>
</AuthContainer>
<AuthBanner>大圖片</AuthBanner>
</AuthPage>
);
}
const AuthPage = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
`;
const AuthContainer = styled.div`
background-color: ${({ theme }) => theme.containerBackground};
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
align-items: center;
width: 50%;
margin-top: 120px;
`;
const AuthBanner = styled.div`
display: flex;
width: 50%;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const AuthButton = styled.button`
border-radius: 5px;
background-color: #217c4a;
border: none;
cursor: pointer;
color: white;
min-width: 300px;
font-family: "Noto Sans TC", sans-serif;
font-weight: bold;
padding: 6px 0;
margin: 2rem 0;
&.hover {
cursor: pointer;
}
`;
const AuthTitle = styled.div`
width: 100%;
text-align: center;
font-size: 24px;
font-weight: bold;
text-align: start;
`;
const AuthLink = styled.div`
width: 100%;
display: flex;
justify-content: center;
text-align: center;
`;
I have try useEffect but it doesn’t work too , I tried this over 4 hours …
2
Answers
I don't know if this is a good approach.
I use DOM for current value and update DOM
I feel like it’s not necessary to maintain
confirmCheck
as a state, you could just create a function:Using this function to check instead of state could solve the potential sync issues with state