Original question
I have the following component
import React from 'react';
import PropTypes from 'prop-types';
import { useForm } from 'react-hook-form';
export default function FormField({ type, name, formRegister, errorMsg, state }) {
const methods = useForm();
const { register } = methods;
return (
<>
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900">
Password
</label>
<div className="mt-1">
<input
type={type}
name={name}
{...register({ formRegister }, { required: { errorMsg } })}
className="py-3 px-4 block w-full shadow-sm text-gray-900 focus:ring-blue-700 focus:border-blue-900 border-gray-300 rounded-md"
onChange={(event) => {state}(event.target.value)}
/>
</div>
</div>
</>
);
}
FormField.PropTypes = {
type: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
formRegister: PropTypes.string.isRequired,
errorMsg: PropTypes.string.isRequired,
state: PropTypes.string.isRequired
};
FormField.defaultProps = {
type: 'text',
name: 'text',
formRegister: 'text',
errorMsg: 'text is required',
state: 'setName'
};
My goal for this component is to create a dynamic field component so I can do the following
<FormField
type="password"
name="password"
formRegister="password"
errorMsg="Password is required"
state="setPassword"
/>
However, I'm having issues in passing in the react-form-hooks ...register
I get the following
TypeError: path.split is not a function
Any help would be great, cheers.
original edit
So the forms are now working thanks too knoefel, however, here is the issue, the error messages are now not showing.
So in my component I have everything needed for errors to be passed, however they are not showing, debugging this I've found that when I do this
<FormField
{...register(email, { required: 'email is required' })}
type="email"
label="Email"
name="email"
errorMsg="Email is required"
placeholder="[email protected]"
/>
The error now shows, so what gives? I already have this in the component?
Updated question #2
I now have the following component
import React from 'react';
import PropTypes from 'prop-types';
const FormField = React.forwardRef(
({ type, name, label, required, placeholder, ...props }, ref) => {
return (
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900">
{label}
<span className="text-red-500 font-bold text-lg">{required && '*'}</span>
</label>
<div className="mt-1">
<input
{...props}
name={name}
ref={ref}
type={type}
id={name}
className={['field', `field--${type}`].join(' ')}
placeholder={placeholder}
/>
</div>
</div>
);
}
);
export default FormField;
FormField.propTypes = {
type: PropTypes.oneOf(['text', 'email', 'password', 'file', 'checkbox']),
register: PropTypes.func,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
placeholder: PropTypes.string
};
FormField.defaultProps = {
type: 'text',
name: 'text',
label: 'Label',
placeholder: ''
};
and now the following page
import Head from 'next/head';
import Link from 'next/link';
import Image from 'next/image';
import Background from '../../../public/images/option1.png';
import Router from 'next/router';
import { signIn } from 'next-auth/client';
import { useForm, FormProvider } from 'react-hook-form';
// components
import ErrorsPopup from '../../components/ErrorsPopup';
import FormField from '../../components/Forms/FormField';
import Button from '../../components/Button';
export default function Login() {
const methods = useForm();
const { handleSubmit, register } = methods;
const onSubmit = async (data) => {
await signIn('credentials', {
redirect: false,
data
});
Router.push('/dashboard');
};
return (
<>
<Head>
<title>Ellis Development - Login</title>
</Head>
<div className="relative">
<div className="md:flex">
{/* Image */}
<div className="flex items-center justify-center bg-blue-700 h-screen lg:w-96">
<Image src={Background} width={350} height={350} layout="fixed" />
</div>
{/* Contact form */}
<div className="flex flex-col justify-center px-6 sm:px-10 w-full">
<h1 className="text-4xl font-extrabold text-grey-800">Login</h1>
{/* errors */}
<FormProvider {...methods}>
<ErrorsPopup />
</FormProvider>
<form
onSubmit={handleSubmit(onSubmit)}
className="mt-6 flex flex-col gap-y-6 sm:gap-x-8">
{/* email field */}
<FormField
{...register('email', {
required: 'Email is required',
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i
})}
type="email"
label="Email"
placeholder="[email protected]"
required
/>
{/* password field */}
<FormField
{...register('password', {
required: 'Password is required'
})}
type="password"
label="Password"
placeholder="*******"
required
/>
<div className="flex items-center justify-between sm:col-span-2">
<div>
<Button type="submit" label="Login" icon="SaveIcon" />
</div>
<div>
<Link href="/dashboard/auth/register">
<a className="underline decoration-blue-500 decoration-4 hover:decoration-2 mr-4">
Register
</a>
</Link>
<Link href="/dashboard/auth/forgot">
<a className="underline decoration-blue-500 decoration-4 hover:decoration-2">
Forgot your password?
</a>
</Link>
</div>
</div>
</form>
</div>
</div>
</div>
</>
);
}
The issue here now is that the form is still not submitting.
Any help would be great, cheers.
edit #2
This is what I get when submitting the form and also added console.log(formState.errors)
Also when I fill the form out I get no errors, so could the issue be around on the onSubmit?.
I made a working CodeSandbox after your last update of your question. You forgot to set the name prop of your <input />
, so RHF had no chance to access the form fields. You also don't need useState
here, as you can just access the form data in the callback of handleSubmit
.
You could also simplify your interface by using your name
prop as the first argument for your register
call. The spread of register
will also return a name
property set to the name of the first argument you pass to register
.
export default function Login() {
const methods = useForm();
const { handleSubmit, register } = methods;
const onSubmit = async (data) => {
console.log(data);
};
return (
<>
<Head>
<title>Ellis Development - Login</title>
</Head>
<div className="relative">
<div className="md:flex">
{/* Image */}
<div className="flex items-center justify-center bg-blue-700 h-screen lg:w-96"></div>
{/* Contact form */}
<div className="flex flex-col justify-center px-6 sm:px-10 w-full">
<h1 className="text-4xl font-extrabold text-grey-800">Login</h1>
<FormProvider {...methods}>{/* errors */}</FormProvider>
<form
onSubmit={handleSubmit(onSubmit)}
className="mt-6 flex flex-col gap-y-6 sm:gap-x-8"
>
{/* email field */}
<FormField
{...register("email", {
required: "Email is required",
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i
})}
type="email"
label="Email"
placeholder="[email protected]"
required
/>
{/* password field */}
<FormField
{...register("password", {
required: "Email is required"
})}
type="password"
label="Password"
placeholder="*******"
required
/>
<div className="flex items-center justify-between sm:col-span-2">
<div>
<input type="submit" label="Login" />
</div>
<div>
<Link href="/dashboard/auth/register">
<a className="underline decoration-blue-500 decoration-4 hover:decoration-2 mr-4">
Register
</a>
</Link>
<Link href="/dashboard/auth/forgot">
<a className="underline decoration-blue-500 decoration-4 hover:decoration-2">
Forgot your password?
</a>
</Link>
</div>
</div>
</form>
</div>
</div>
</div>
</>
);
}
const FormField = forwardRef(
({ type, name, label, required, placeholder, ...props }, ref) => {
return (
<div>
<label
htmlFor={name}
className="block text-sm font-medium text-gray-900"
>
{label}
<span className="text-red-500 font-bold text-lg">
{required && "*"}
</span>
</label>
<div className="mt-1">
<input
{...props}
name={name}
ref={ref}
type={type}
id={name}
className={["field", `field--${type}`].join(" ")}
placeholder={placeholder}
/>
</div>
</div>
);
}
);