'React hook form v7 Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()

Getting the error in browser Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()

enter image description here

My code:

import { yupResolver } from '@hookform/resolvers/yup'
import { useState } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { contactSchema } from 'schemas/schemas'
import { InputFloatLabel } from './components/Inputs/InputFloatLabel'

type TypeFormInput = {
  name: string
  email: string
  textarea: string
}

export const Register = () => {
  const [isLoading, setIsLoading] = useState(false)

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<TypeFormInput>({ resolver: yupResolver(contactSchema) })

  const onSubmit: SubmitHandler<TypeFormInput> = async ({ name, email }) => {
    console.log('🚀 ~ file: Register.tsx ~ line 25 ~ email', email)
    console.log('🚀 ~ file: Register.tsx ~ line 25 ~ name', name)
  }


  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <InputFloatLabel
            type="text"
            placeholder="Name"
            {...register('name')}
          />

          <button type="submit">{isLoading ? 'Loading' : 'Send Mail'}</button>
        </div>
      </form>
    </div>
  )
}

And the Input comp:

import { useState } from 'react'

type typeInput = {
  placeholder: string
  type?: string
}

export const InputFloatLabel: React.FC<typeInput> = ({ type, placeholder, ...props }) => {
  const [isActive, setIsActive] = useState(false)

  const handleTextChange = (text: string) => {
    if (text !== '') setIsActive(true)
    else setIsActive(false)
  }

  return (
    <div>
      <input
        {...props}
        id={placeholder}
        type={placeholder ? placeholder : 'text'}
        onChange={(e) => handleTextChange(e.target.value)}
      />
      <label htmlFor={placeholder}>
        {placeholder ? placeholder : 'Placeholder'}
      </label>
    </div>
  )
}

I don't have this issue with ChakraUI that I've built but now just doing plain input as a separate component getting that issue.

I have tried some suggestions from here, but still can't fix it: https://github.com/react-hook-form/react-hook-form/issues/85



Solution 1:[1]

So the issue is that I think that the {...register("name"}} line actually includes a ref property. You could console.log that out to verify; this is what I found to be true when using {...field} with the ControlledComponent. A very quick fix to get rid of the console error is to just, after the line with the spread, to add a ref={null} to override this ref that is being passed in from the library.

Solution 2:[2]

You forgot to forward the ref in your InputFloatLabel. See https://reactjs.org/docs/forwarding-refs.html

You you case it would look like this:

export const InputFloatLabel: React.FC<typeInput> =
    // Use React.forwardRef
    React.forwardRef(({type, placeholder, ...props}, ref) => {
      const [isActive, setIsActive] = useState(false)

      const handleTextChange = (text: string) => {
        if (text !== '') setIsActive(true)
        else setIsActive(false)
      }

      return (
        <div>
          <input
            ref={ref /* Pass ref */}
            {...props}
            id={placeholder}
            type={placeholder ? placeholder : 'text'}
            onChange={(e) => handleTextChange(e.target.value)}
          />
          <label htmlFor={placeholder}>
            {placeholder ? placeholder : 'Placeholder'}
          </label>
        </div>
      )
    })

Solution 3:[3]

In https://react-hook-form.com/faqs, scroll to "How to share ref usage?" may help?

import React, { useRef } from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit } = useForm();
  const firstNameRef = useRef(null);
  const onSubmit = data => console.log(data);
  const { ref, ...rest } = register('firstName');

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...rest} name="firstName" ref={(e) => {
        ref(e)
        firstNameRef.current = e // you can still assign to ref
      }} />

      <button>Submit</button>
    </form>
  );
}

Solution 4:[4]

the field element and register function pass a ref to the element. If you define a custom React Component and try to use it within a controller or with register, funky things can happen. I found using React.forwardRef() solves the problem when using Custom Components.

CustomSwitchComponent.tsx

import React from 'react';
import {FormControlLabel, Switch, SxProps, Theme} from "@mui/material";

const TradingStrategyEditFormInstructionsInputSwitch:
    // Note this type allows us to do React.forwardRef(Component)
    React.ForwardRefRenderFunction<HTMLButtonElement, {
        label: string,
        checked: boolean,
        onBlur: React.FocusEventHandler<HTMLButtonElement>
    }> = ({label, ...rest}) => {
    // Spreading ...rest here will pass the ref :)
    return <FormControlLabel control={<Switch {...rest} />}
        labelPlacement={"top"}
        label={label}
    />;
};

// *Huzzah!*
export default React.forwardRef(TradingStrategyEditFormInstructionsInputSwitch);

CustomSwitchController.tsx

<Controller
    control={formCtx.control}
    name={fieldPath}
    key={type + side + key}
    render={({field}) => {
        return <TradingStrategyEditFormInstructionsInputField
            {...field}
            label={key}
            checked={field.value}
            onBlur={() => {
                field.onBlur()
                handleOnBlur(key)
            }}
        />
    }}/>

Idk if we're allowed to link YT Videos, but techsith has a great video on using forwardRef with useRef that should clear things up. https://www.youtube.com/watch?v=ScT4ElKd6eo

Solution 5:[5]

Your input component does not export ref as props since it is a functional component.

 React.useEffect(() => {
    register('name', { required: true });
  }, []);

         <InputFloatLabel
            type="text"
            placeholder="Name"
            name="name"
           // Remove the register from here
          />

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Joel M.
Solution 2
Solution 3 Dang Tran
Solution 4
Solution 5 Monish N