skip to Main Content

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


  1. Chosen as BEST ANSWER

    I don't know if this is a good approach.

    I use DOM for current value and update DOM

    const checkConfirm = () => {
        const password = document.querySelector("#password");
        const confirmPassword = document.querySelector("#confirmPassword");
        console.log(password.value ,confirmPassword.value)
        if (password.value === confirmPassword.value) {
          password.classList.remove("is-invalid");
          password.classList.add("is-valid");
          confirmPassword.classList.remove("is-invalid");
          confirmPassword.classList.add("is-valid");
        } else {
          password.classList.add("is-invalid");
          password.classList.remove("is-valid");
          confirmPassword.classList.add("is-invalid");
          confirmPassword.classList.remove("is-valid");
        }
      };
    
    

  2. I feel like it’s not necessary to maintain confirmCheck as a state, you could just create a function:

    const checkPasswordsMatch = () => form.password === form.confirmPassword;
    

    Using this function to check instead of state could solve the potential sync issues with state

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