reactjs - Hur man implementerar React Hook Form Smart Form-komponent med TypeScript
Hur kan jag ärva typen av ett formulär till dess barn? Jag vill använda autoförslagsfunktionen i namn genom att ärva typen. Och koden fungerar bra. Men det fungerar inte som jag vill.
Jag gjorde det genom att hänvisa till länken nedan. Som referens använder CSS Tailwind.
https://react-hook-form.com/advanced-usage#SmartFormComponent
kod :
// Form.tsx
import { Children, createElement, FormHTMLAttributes, ReactElement } from "react";
import { FormProvider, SubmitHandler, useForm, UseFormProps } from "react-hook-form";
interface FormProps<T> extends Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
form: UseFormProps<T>;
children: ReactElement | ReactElement[];
onSubmit: SubmitHandler<T>;
}
export default function Form<T>({ children, onSubmit, form, ...rest }: FormProps<T>): JSX.Element {
const methods = useForm<T>(form);
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)} {...rest}>
{Children.map(children, (child) => {
return child.props.name
? createElement<T>(child.type, {
...{
key: child.props.name,
...methods.register,
...child.props
}
})
: child;
})}
</form>
</FormProvider>
);
}
// Input.tsx
import { ExclamationCircleIcon } from "@heroicons/react/solid";
import { InputHTMLAttributes } from "react";
import { useFormContext } from "react-hook-form";
import { classNames } from "../../utils/classNames";
interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "id" | "className"> {
name: string;
label?: string;
}
export default function Input({ name, label, ...rest }: InputProps): JSX.Element {
const methods = useFormContext();
return (
<>
<div>
<div>
{label && (
<label htmlFor={name} className="block text-sm font-medium text-gray-700">
{label}
</label>
)}
<div className="mt-1 relative">
<input
id={name}
{...methods.register(name)}
{...rest}
className={classNames(
methods.formState.errors[name]
? "border-red-300 text-red-900 placeholder-red-300 focus:ring-red-500 focus:border-red-500"
: "border-gray-300 placeholder-gray-400 focus:ring-indigo-500 focus:border-indigo-500",
"appearance-none block w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none sm:text-sm"
)}
/>
{methods.formState.errors[name] && (
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<ExclamationCircleIcon className="h-5 w-5 text-red-500" aria-hidden="true" />
</div>
)}
</div>
</div>
{methods.formState.errors[name] && (
<p className="mt-1 text-sm text-red-600">{methods.formState.errors[name].message}</p>
)}
</div>
</>
);
}
// index.tsx
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { Form, Input } from "../form";
interface FormFields {
email: string;
password: string;
}
const schema = yup.object().shape({
email: yup.string().required("Email is required.").email("Email format is incorrect."),
password: yup
.string()
.required("Password is required.")
.matches(
/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])[A-Za-z0-9!@#$%^&*]{8,128}$/,
"Password format is incorrect."
)
});
export default function SignInForm(): JSX.Element {
const onSubmit = async (data: FormFields) => {
console.log(data);
};
return (
<Form<FormFields>
onSubmit={onSubmit}
form={{ resolver: yupResolver(schema), reValidateMode: "onChange" }}
className="space-y-6"
>
<Input name="email" type="email" label="Email address" />
<Input name="password" type="password" label="Password" />
<div>
<button type="submit" className="w-full flex justify-center py-2 px-4 btn btn-indigo">
Sign in
</button>
</div>
</Form>
);
}