4

Good Day,

I have a custom component built with material-ui. Am trying to implement react-hook-form. I am registering the components using React forwardRef. React hook form does fire and displays the errors as expected. But when I enter text into textbox the state updates via useState hook but the react hook form errors object does not pick this up. In other words I cannot submit the form even though I have correct values.

First is the CustomTextbox component.

import React from "react";
import TextField from '@material-ui/core/TextField';

interface Props {
  id: string;
  label: string,
  variant: "filled" | "standard" | "outlined",
  value: string, 
  setValue: React.Dispatch<React.SetStateAction<string>>,
  disabled?: boolean
}

const CustomTextBox: React.FC<Props> = React.forwardRef(({id, label, variant, value, setValue, 
  disabled=false}, ref) => {


const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {                
    setValue(e.currentTarget.value);
  }
  return (
    <TextField id={id} label={label} value={value} variant={variant} onChange={e => 
      handleChange(e)} disabled={disabled} inputRef={ref} />
  );
}) 

export default CustomTextBox;

Next is how is use this component with react-hook-form

import { ErrorMessage } from "@hookform/error-message";
import { Subtitle2 } from "@material/react-typography";
import React, { useState } from "react";
import { useForm, SubmitHandler } from "react-hook-form";
import { CustomButton } from "../HtmlComponents/CustomButton";
import CustomTextBox from "../HtmlComponents/CustomTextBox";

const UseComponent: React.FunctionComponent = () => {
  const [jobTitle, setJobTitle] = useState("");

  type Inputs = {
    jobTitle: string
  }

    const { register, handleSubmit, watch,  formState: { errors } } = useForm<Inputs>();
    const onSubmit: SubmitHandler<Inputs> = data => console.log(data);

    useEffect(() => {
        console.log(jobTitle);
    }, [jobTitle])

  return(
    <form  onSubmit={handleSubmit(onSubmit)}>
      <CustomTextBox id="jobTitle" variant="filled" label="Given Names *" value={jobTitle} 
         setValue={setJobTitle} {...register("jobTitle", {required: "Job title is required"})} />                                               
      <ErrorMessage errors={errors} name="jobTitle" render={({ message }) => <Subtitle2 style= 
         {{color: "red"}}>{message}</Subtitle2>} />                             
      <CustomButton id="submit" label="Save" variant="contained"  submit={true} 
        disabled={false} />         
    </form>
  )
}

export default UseComponent

This is obviously not the entire page but just a single example of trying to re-use the custom component with react-hook-form.

When I submit the error message gets displayed which is expected but I also expect the error to disappear when I enter text in the textbox.

Also please note that the useEffect on jobTile does fire and it gets console logged whenever I type in the textbox. So that measns the useState function passed to custome component does fire and updates the state.

The problem I have is that react-hook-form does not pickup this state change and hence the errors remain.

I'm pretty sure im doing something but I cant pick it up. I appreciate any and all help give.

Thanks and Hello from South Africa.

1 Answer 1

5

You have to use Controller when working with Material-UI and react-hook-form.

References:

  1. https://react-hook-form.com/get-started#IntegratingwithUIlibraries
  2. https://react-hook-form.com/get-started#IntegratingControlledInputs

Edit: If you use Controller then your child component should look like this

interface Props {
  id: string;
  label: string,
  variant: "filled" | "standard" | "outlined",
  disabled?: boolean
  control:UseControllerProps['control'] // Control prop to pass from Parent Component
  name:string // name of field to register with react hook form
}

const CustomTextBox: React.FC<Props> = (
     ({id, label, variant, disabled=false, control, name}) => {
     const { field } = useController({name, control});

// This is not required. React Hook Form will handle onChange events
// const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement | //HTMLInputElement>) =>             
  //  {    setValue(e.currentTarget.value);}

  return (
           <TextField 
              id={id} 
              label={label} 
              variant={variant}  
              disabled={disabled} 
              {...field}
           />
         }
     />
  );
}) 

In your Parent component, destructure control from useForm hook

const { handleSubmit, watch,  formState: { errors }, control } = useForm<Inputs>();

UseController Documentation : https://react-hook-form.com/api/usecontroller

Sign up to request clarification or add additional context in comments.

6 Comments

Hi I'm having issues with passing the control from page to component. What is the type. Not even sure if im doin the right thing
Please check the updated answer using Controller
Cool thanks for you answer. I'm trying to implement your answer but what is the type of Inputs (control:Control<Inputs,any>). As this will be different for every form ?
I Tried the following but the compile complains. control: Control<PersonalInfoInputs,any> | Control<AdditionalInfoInputs,any>,
Control type is UseFormReturn['control']/UseControllerProps['control']. I've updated code to use useController hook and also types.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.