Understanding: Warning: Function components cannot be given refs
Asked Answered
C

2

22

I know that refs are used to access DOM elements directly without altering the state. I read that it's not possible to give refs to function components because they don't have a state.

Refs cannot be attached to function components. Although, we can define refs and attach them to either DOM elements or class components. The bottom line is — function components do not have instances so you can’t reference them.

taken from: https://blog.logrocket.com/cleaning-up-the-dom-with-forwardref-in-react/

I still don't understand.

I am using the Tooltip component from Ant Design (https://ant.design/components/tooltip/), the Button component and a custom CircleButton component.

Given the following JSX:

<Tooltip placement="left" title={"Lock slot"}>
  <CircleButton onClick={() => execute(client.close)} icon={<LockOutlined />} disabled={loading} />
</Tooltip>

And my CircleButton component. Used like this, it will generate the warning.

const CircleButton = (props) => // gives the warning
  <Button shape="circle" size="small" style={{ marginRight: "8px" }} {...props} />

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

Note that everything works as expected despite the warning.

If I edit it as follows though, it will work fine, why?

const CircleButton = forwardRef((props, ref) => // doesn't give the warning
  <div ref={ref}>
    <Button shape="circle" size="small" style={{ marginRight: "8px" }} {...props} />
  </div>)

Does the div component have a state? I don't get it. Is the forwardRef doing some magic and creating a state for the div element?

Why then if I pass the ref to the Button component it still gives the warning?

const CircleButton = forwardRef((props, ref) => // gives the warning
  <Button ref={ref} shape="circle" size="small" style={{ marginRight: "8px" }} {...props} />)

If I pass the antd Button directly as a child, it works. But this is because I suppose that the antd button has a state hence it can have refs.

<Tooltip placement="left" title={"Lock slot"}> // doesn't give the warning
  <Button shape="circle" size="small" style={{ marginRight: "8px" }} />
</Tooltip>
Crackling answered 27/4, 2020 at 2:19 Comment(2)
A different use case but similar error message: https://mcmap.net/q/127090/-how-do-i-avoid-39-function-components-cannot-be-given-refs-39-when-using-react-router-domShelba
Here I explain the "why" of this warning for a similar scenario with an input element which helped me to fully understand it: https://mcmap.net/q/591645/-why-does-custom-input-component-cause-quot-function-components-cannot-be-given-refs-quot-warningMicrowave
N
9

As the warning states you cannot assign refs to functional components without the usage of forwardRef.

In order to have access to refs of any component, its required that an instance of the component is created and an instance is only created for class components, while functional components are invoked or called

From v16.8.0 onwards React introduced an API called useRef which allows you to create refs within functional components which can be used on HTML nodes, class components or functional components wrapped with forwardRef


In addition: To achieve the same behavior that is available in class components with ref, you can use forwardRef with useImperativeHandle hook to expose certain functions or states from within the functional component to the parent component

const CircleButton = forwardRef((props, ref) => {
  const someFunction = () =>{}
  useImperativeHandle(ref, () => ({
     someFunc
  }));

  return (
    <div>
        <Button shape="circle" size="small" style={{ marginRight: "8px" }} {...props} />
   </div>

  )
}
Nucleolar answered 27/4, 2020 at 6:9 Comment(0)
H
5

Don't be confused, first this it's not related to functional or class components issue means you can use ref for both, react 16+ has hook useRef so you can used ref for functional components also,

Answer to your question, antd Button has their own ref so it's omitting the ref passed by parent component in you case Tooltip that why your are not seeing any warning for that but when you used your own component that time you have to take of ref passed by Tooltip.

And, still you don't want to use React.forwordRef then simply ignore it while passing props to your component. but then you don't get privileged some feature provided by antd controlled components

Hyo answered 27/4, 2020 at 3:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.