How do I avoid 'Function components cannot be given refs' when using react-router-dom?

ReactjsReact RouterMaterial Ui

Reactjs Problem Overview


I have the following (using Material UI)....

import React from "react";
import { NavLink } from "react-router-dom";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
function LinkTab(link){
    return <Tab component={NavLink}
        to={link.link}
        label={link.label}
        value={link.link}
        key={link.link}
    />;
}

In the new versions this causes the following warning...

> Warning: Function components cannot be given refs. Attempts to access > this ref will fail. Did you mean to use React.forwardRef()? > > Check the render method of ForwardRef. > in NavLink (created by ForwardRef)

I tried changing to...

function LinkTab(link){
    // See https://material-ui.com/guides/composition/#caveat-with-refs
    const MyLink = React.forwardRef((props, ref) => <NavLink {...props} ref={ref} />);
    return <Tab component={MyLink}
        to={link.link}
        label={link.label}
        value={link.link}
        key={link.link}
    />;
}

But I still get the warning. How do I resolve this issue?

Reactjs Solutions


Solution 1 - Reactjs

NavLink from react-router is a function component that is a specialized version of Link which exposes a innerRef prop for that purpose.

// required for react-router-dom < 6.0.0
// see https://github.com/ReactTraining/react-router/issues/6056#issuecomment-435524678
const MyLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />);

You could've also searched our docs for react-router which leads you to https://mui.com/getting-started/faq/#how-do-i-use-react-router which links to https://mui.com/components/buttons/#third-party-routing-library. The last link provides a working example and also explains how this will likely change in react-router v6

Solution 2 - Reactjs

Just give it as innerRef,

// Client.js
<Input innerRef={inputRef} />

Use it as ref.

// Input.js
const Input = ({ innerRef }) => {
  return (
    <div>
      <input ref={innerRef} />
    </div>
  )
}

Solution 3 - Reactjs

You can use refs instead of ref. This only works as it avoids the special prop name ref.

<InputText
  label="Phone Number"
  name="phoneNumber"
  refs={register({ required: true })}
  error={errors.phoneNumber ? true : false}
  icon={MailIcon}
/>

Solution 4 - Reactjs

If you find that you cannot add a custom ref prop or forwardRef to a component, I have a trick to still get a ref object for your functional component.

Suppose you want to add ref to a custom functional component like:

 const ref = useRef();

 //throws error as Button is a functional component without ref prop
 return <Button ref={ref}>Hi</Button>;

You can wrap it in a generic html element and set ref on that.

 const ref = useRef();

 // This ref works. To get button html element inside div, you can do 
 const buttonRef = ref.current && ref.current.children[0];
 return (
  <div ref={ref}>
   <Button>Hi</Button>
  </div>
 );

Of course manage state accordingly and where you want to use the buttonRef object.

Solution 5 - Reactjs

In our case, we were was passing an SVG component (Site's Logo) directly to NextJS's Link Component which was a bit customized and we were getting such error.

Header component where SVG was used and was "causing" the issue.

import Logo from '_public/logos/logo.svg'
import Link from '_components/link/Link'

const Header = () => (
  <div className={s.headerLogo}>
    <Link href={'/'}>
      <Logo /> 
    </Link>
  </div>
)

Error Message on Console

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

Customized Link Component

import NextLink from 'next/link'
import { forwardRef } from 'react'

const Link = ({ href, shallow, replace, children, passHref, className }, ref) => {
  return href ? (
    <NextLink
      href={href}
      passHref={passHref}
      scroll={false}
      shallow={shallow}
      replace={replace}
      prefetch={false}
      className={className}
    >
      {children}
    </NextLink>
  ) : (
    <div className={className}>{children}</div>
  )
}

export default forwardRef(Link)

Now we made sure we were using forwardRef in the our customized Link Component but we still got that error.

In order to solve it, I changed the wrapper positioning of SVG element to this and :poof:

const Header = () => (
  <Link href={'/'}>
    <div className={s.headerLogo}>
      <Logo />
    </div>
 </Link>
)

Solution 6 - Reactjs

to fix this warning you should wrap your custom component with forwardRef function as mention in this blog very nicely

    const AppTextField =(props) {return(/*your component*/)}

change following code to

const AppTextField = forwardRef((props,ref) {return(/*your component*/)}

Solution 7 - Reactjs

const renderItem = ({ item, index }) => {

		return (
			<>			
			<Item
				key={item.Id}
				item={item}
				index={index}
			/>
			</>
		);
	};

Use Fragment to solve React.forwardRef()? warning

Solution 8 - Reactjs

I just paste here skychavda solution, as it provide a ref to a child : so you can call child method or child ref from parent directly, without any warn.

source: https://github.com/reactjs/reactjs.org/issues/2120

/* Child.jsx */
import React from 'react'

class Child extends React.Component {
  componentDidMount() {
    const { childRef } = this.props;
    childRef(this);
  }
  componentWillUnmount() {
   const { childRef } = this.props;
    childRef(undefined);
  }
  alertMessage() {
    window.alert('called from parent component');
  }
  render() {
    return <h1>Hello World!</h1>
  }
}

export default Child;
/* Parent.jsx */
import React from 'react';
import Child from './Child';

class Parent extends React.Component {
  onClick = () => {
    this.child.alertMessage(); // do stuff
  }
  render() {
    return (
      <div>
        <Child childRef={ref => (this.child = ref)} />
        <button onClick={this.onClick}>Child.alertMessage()</button>
      </div>
    );
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJGleasonView Question on Stackoverflow
Solution 1 - ReactjsepsilonView Answer on Stackoverflow
Solution 2 - ReactjsHasan Sefa OzalpView Answer on Stackoverflow
Solution 3 - ReactjsInamur RahmanView Answer on Stackoverflow
Solution 4 - ReactjsSnebhuView Answer on Stackoverflow
Solution 5 - ReactjsKeshavDulalView Answer on Stackoverflow
Solution 6 - ReactjsMohd QasimView Answer on Stackoverflow
Solution 7 - ReactjsKeshav GeraView Answer on Stackoverflow
Solution 8 - Reactjsboly38View Answer on Stackoverflow