I am using react-hook-form for validating forms in react. This is how I did it so far. I created a component wrapping a TextField
with a react-hook-form Controller
component. Then I used in my form.
Component: TextfieldController
import { Controller } from "react-hook-form";
const TextfieldController = ({
control,
errors,
id,
label,
placeholder = "",
size = 50,
...otherProps
}: TextfieldControllerProps) => {
return (
<Field>
<Controller
name={id}
control={control}
render={({ field }) => (
<TextField
{...field}
{...otherProps}
id={id}
label={label}
size={size}
placeholder={placeholder}
/>
)}
/>
{errors[id] && <Error>{errors[id]?.message?.toString()}</Error>}
</Field>
)
}
Usage:
<FormView title="API Artifact" onClose={handleOnClose}>
<TextfieldController
required
autoFocus
id="apiName"
label="Name"
placeholder="Name"
control={control}
errors={errors}
/>
<TextfieldController
required
id="apiContext"
label="Context"
placeholder="Context"
control={control}
errors={errors}
/>
// continue...
</FormView>
In this case control
and errors
props should be provided for every usage of TextfieldController
component, even both props are same for the entire form. Is there a way to avoid this?
I tried it using a custom hook. Pass control
and errors
when getting the TextfieldController
component from the hook and pass other props when using. But it causes to lose focus on onChange
due to re-rendering.
const { TextfieldController } = useTextfieldController({ control, errors });
I don't want to exactly use this custom hook if there is a better way. Is there a better way to do this?
You can use the useFromContext
hook to access both control
and errors
inside your TextfieldController
.
import { Controller, useFormContext } from "react-hook-form";
const TextfieldController = ({
id,
label,
placeholder = "",
size = 50,
...otherProps
}) => {
const { formState: { errors }, control } = useFormContext();
return (
<Field>
<Controller
name={id}
control={control}
render={({ field }) => (
<TextField
{...field}
{...otherProps}
id={id}
label={label}
size={size}
placeholder={placeholder}
/>
)}
/>
{errors[id] && <Error>{errors[id]?.message?.toString()}</Error>}
</Field>
);
};
As written in the documentation, you will need to wrap your form with a formProvider
.
You could do it for example in your FormView
component.