To be clear:
- You can use refs in function components just fine (usually via
useRef).
- You can't use a ref directly on a function component if that function component doesn't forward the ref to one of its children.
So if you try to use <X ref={ref} /> and X is a function component that doesn't forward refs, you'll get an error:
const { useState } = React;
const X = (props) => {
return <div>ExampleFunction</div>;
};
class App extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
render() {
return <div>
<X ref={this.ref}/>
</div>;
}
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
That's because the "instance" of a function component is an implementation detail of React itself (basically an array of hook data storage slots), not something React gives you access to.
But the function component can forward the ref to one of its children. Here's an example of the distinction between a ref on a class component and a ref on a function component that forwards it:
const { useState } = React;
class ExampleClass extends React.Component {
render() {
return <div>ExampleClass</div>;
}
}
const ExampleFunction = React.forwardRef((props, ref) => {
return <div ref={ref}>ExampleFunction</div>;
});
class App extends React.Component {
constructor(props) {
super(props);
this.ref1 = React.createRef();
this.ref2 = React.createRef();
}
componentDidMount() {
console.log("ref1 is an instance of ExampleClass", this.ref1.current instanceof ExampleClass); // true
console.log("ref2 is an instance of HTMLElement:", this.ref2.current instanceof HTMLElement); // true
}
render() {
return <div>
<ExampleClass ref={this.ref1}/>
<ExampleFunction ref={this.ref2}/>
</div>;
}
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
As you can see, with the class component you actually get a reference to the instance of the class. With the function component, you get a reference to the child it forwarded the ref to.
Does putting hooks into a function component cause it to have an instance?
Honestly I couldn't tell you whether function components have "instances" (in a loose sense) if you don't use hooks in them or not; I suspect they do, though, for various pieces of React internal information (if you want to dig through the source, you can find out for sure). But unlike a class component, you can't directly access that internal "instance."