React/ReactJS: Refs
In React's one-way (or "unidirectional') flow of data, the parent components interact with children via props.
However, in some cases you might need to directly access the attributes of some elements of a child component and/or modify them, without re-rendering them with updated props. This is where refs come in handy. Refs are kind of attributes that offer access to React elements created by the render()
method. Refs modify the actual DOM contrast to the virtual DOM philosphy of ReactJS.
The official ReactJS documentation here cites the following three reasons to use refs:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.
We create refs using the React.createRef()
API (introduced in React 16.3, March 2018), which are then added to the JSX elements with the ref
attribute. The below code illustrates how it is used.
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.someRef = React.createRef();
}
render() {
return <div ref={this.someRef} />
}
}
The passed ref
offers access to the referenced node with the current
attribute.
const node = this.someRef.current;
In the below section, we make use of ref's current
attribute to auto-focus on an <input/>
element.
Using Ref to Focus in an Input Element
The ref
attribute is passed to the <input/>
DOM element. Using this ref's current
attribute, the <input/>
element's focus()
method is invoked inside componentDidMount()
to automatically focus on it right after the component loads.
class TextFocus extends React.Component{
constructor(props) {
super(props);
this.nameInput = React.createRef();
}
componentDidMount() {
this.nameInput.current.focus();
}
render() {
return(
<div>
<input
ref={this.nameInput}
placeholder="Name"
/>
</div>
);
}
}
Callback Refs
There is another way to set refs, without the need of passing any ref
attribute created by the createRef()
API in the constructor. Instead, a function receiving an HTML DOM element or an instance of a component as an argument is passed. It typically looks as follows:
<input type="text" ref={el => this.textInput = el}/>
In the following section, we will make use of callback ref to reference a <video>
element and invoke its play()
and pause()
methods.
Using Callback Ref to Play/Pause an HTML5 Video
For the purpose of this example, let us import some video of a bird (bird.mp4
) inside the /videos
directory. I downloaded this silent video for free from https://videos.pexels.com/videos/video-of-a-bird-855097
import bird from './videos/bird.mp4';
This import name will be substituted as a value for the src
attribute of the <source>
element.
A callback function is passed to the ref inside the <video>
element. This callback stores a reference to this VIDEO
node in an instance property.
class Video extends React.Component {
constructor(props) {
super(props);
this.state = {
play: false,
}
this.videoClick = this.videoClick.bind(this);
}
videoClick() {
this.state.play? this.video.pause():this.video.play();
this.setState({ play: !this.state.play });
}
render() {
return (
<div>
<video ref={vd => this.video = vd}>
<source
src={bird}
type="video/mp4"
/>
</video>
<div>
<button onClick={this.videoClick}>Play / Pause</button>
</div>
</div>
)
}
}
export default Video;
Note that we do not put the controls
attribute into the <source>
element, which would have enabled video controls like play/pause, full-screen toggle, volume adjustments and slider. Instead, we added an additional Play /Pause
button below which on clicked makes use of the this.video
callback inside its handler to toggle the call between the video element's play()
and pause()
methods.
Upon rendering, the loaded <Video/>
component appears as follows.
Adding Ref to a Class Component
Let us create a class component called <Name/>
which has a function called setName()
that sets the value of the <input/>
element to some string ("Felix" here). Notice that nowhere we have called this function inside the component.
class Name extends React.Component{
constructor(props) {
super(props);
this.nameInput = React.createRef();
this.setName = this.setName.bind(this);
}
setName() {
this.nameInput.current.value = 'Felix';
}
render() {
return(
<div>
<input
ref={this.nameInput}
placeholder="Name"
/>
</div>
);
}
}
Instead, we call the function from a parent component called <AssignName/>
by attaching a ref.
class AssignName extends React.Component{
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.setName();
}
render() {
return (
<Name ref={this.textInput} />
);
}
}
By the way, if both the above components are in the same file, do not forget to export the parent component <AssignName/>
.
export default AssignName;
If the <Name/>
component above would have been functional (stateless) instead of a class, ref
would not have worked as they do not have instances.
Ref with jQuery, a Third-Party DOM Library
Refs are also used when integrating third-party DOM manipulating libraries like jQuery with React. They are attached to the plugin's wrapper element, which usually is the only child element of the wrapper <div/>
, so as to avoid conflicts with React updates. We have a separate tutorial on using jQuery plugins with React here.
Notes
- Refs should not be overused; avoid them wherever the desired functionality can be achieved declaratively.
-
The
ref
attribute should not be used on functional components as they do not have instances.