In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.
There are a few good use cases for refs:
Avoid using refs for anything that can be done declaratively.
For example, instead of exposing open()
and close()
methods on a Dialog
component, pass an isOpen
prop to it.
React supports a special attribute that you can attach to any component. The ref
attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.
When the ref
attribute is used on an HTML element, the ref
callback receives the underlying DOM element as its argument. For example, this code uses the ref
callback to store a reference to a DOM node:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
React will call the ref
callback with the DOM element when the component mounts, and call it with null
when it unmounts.
Using the ref
callback just to set a property on the class is a common pattern for accessing DOM elements. The preferred way is to set the property in the ref
callback like in the above example. There is even a shorter way to write it: ref={input => this.textInput = input}
.
When the ref
attribute is used on a custom component declared as a class, the ref
callback receives the mounted instance of the component as its argument. For example, if we wanted to wrap the CustomTextInput
above to simulate it being clicked immediately after mounting:
class AutoFocusTextInput extends React.Component {
componentDidMount() {
this.textInput.focus();
}
render() {
return (
<CustomTextInput
ref={(input) => { this.textInput = input; }} />
);
}
}
Note that this only works if CustomTextInput
is declared as a class:
class CustomTextInput extends React.Component {
// ...
}
You may not use the ref
attribute on functional components because they don't have instances:
function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
render() {
// This will *not* work!
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
);
}
}
You should convert the component to a class if you need a ref to it, just like you do when you need lifecycle methods or state.
You can, however, use the ref
attribute inside a functional component as long as you refer to a DOM element or a class component:
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
Your first inclination may be to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. See the Lifting State Up guide for examples of this.
If you worked with React before, you might be familiar with an older API where the ref
attribute is a string, like "textInput"
, and the DOM node is accessed as this.refs.textInput
. We advise against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases. If you're currently using this.refs.textInput
to access refs, we recommend the callback pattern instead.
If the ref
callback is defined as an inline function, it will get called twice during updates, first with null
and then again with the DOM element. This is because a new instance of the function is created with each render, so React needs to clear the old ref and set up the new one. You can avoid this by defining the ref
callback as a bound method on the class, but note that it shouldn't matter in most cases.