Lyle Phillips
Lyle Phillips

Reputation: 377

React hook form errors not updating when component state changes

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.

Upvotes: 4

Views: 16750

Answers (1)

Sreekar
Sreekar

Reputation: 286

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

Upvotes: 5

Related Questions