CryNet, я бы сделал как-то так только через class component:
https://codesandbox.io/s/form-exampl...c/LoginForm.js
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
export default function LoginForm() {
const [tokenWasRequested, saveTokenWasRequested] = useState(false);
const validationScheme = Yup.object().shape({
email: Yup.string().required("Required").email("Invalid email address"),
...(tokenWasRequested
? {
password: Yup.string().required("Required"),
code: Yup.number()
.integer()
.positive()
.typeError("You must specify a number")
.required("Required")
}
: {})
});
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
mode: "all",
criteriaMode: "all",
resolver: yupResolver(validationScheme)
});
const onSubmit = (data) => {
if (!tokenWasRequested) {
saveTokenWasRequested(true);
return;
}
console.log(data);
};
const fields = [
{
type: "email",
name: "email",
label: "Email",
props: {
readOnly: tokenWasRequested
}
}
];
if (tokenWasRequested) {
fields.push(
{
type: "password",
name: "password",
label: "Password"
},
{
type: "text",
name: "code",
label: "2FA token"
}
);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((field) => {
const errorMessage = errors?.[field.name]?.message;
return (
<div
key={`form-field--${field.type}-${field.name}`}
className="field-wrapper"
>
<input
{...(field.props ?? {})}
type={field.type}
placeholder={field.label}
className={errorMessage ? "invalid" : undefined}
{...register(field.name)}
/>
{errorMessage ? (
<div className="error-message">{errorMessage}</div>
) : null}
</div>
);
})}
<div className="form-controls">
<button type="submit">
{tokenWasRequested ? "Login" : "Get 2FA token"}
</button>
{tokenWasRequested ? (
<button
type="button"
onClick={() => {
saveTokenWasRequested(false);
}}
>
Request another 2FA token
</button>
) : null}
</div>
</form>
);
}