In this post, I’m going to walk you through how to use refs on a React Component that’s connected to a Redux store using the React-Redux API.

But first, a little context on refs…

No… Not those kinds of refs.

What’s a ref?

If you haven’t used refs, I highly recommend reading the React docs to understand how they work and what they’re used for but in short, refs are a way of storing references to an object on a component instance.

They can be used to store references to either a DOM element or another class component instance¹ so let’s go through these two use cases real quick.

Using refs on DOM elements

class VideoPlayer extends React.Component { constructor() {

super();

this.state = {

isPlaying: false,

}

this.handleVideoClick = this.handleVideoClick.bind(this);

}



handleVideoClick() {

if (this.state.isPlaying) {

this.video.pause();

}

else {

this.video.play();

}

this.setState({ isPlaying: !this.state.isPlaying });

}



render() {

return (

<div>

<video

ref={video => this.video = video}

onClick={this.handleVideoClick}

>

<source

src="some.url"

type="video/ogg"

/>

</video>

</div>

)

}

}

In this code snippet, I’ve created a very basic video player component that plays and pauses a video whenever you click on a video. Since I need to use the play() and pause() methods that live on the HTML5 video element in order to do this, I’ve stored a reference to that element on the component instance using a ref. This way, I can access those methods as needed inside of my click handler. Pretty neat, right?²

Using refs on other component instances

Sometimes, instead of using a ref on a DOM element, you’ll want to use a ref on another component instance. I recently came across this use case when needing to work with Highcharts inside of React. According to the Highcharts blog on their website, here’s how you can use their library to create charts in React:

class HighchartComponent extends React.Component {

componentDidMount() {

this.chart = new Highcharts[this.props.type || 'Chart'](

this.props.container,

this.props.options

);

}



componentWillUnmount() {

this.chart.destroy();

}



render() {

return (

<div id={this.props.container}></div>

)

}

}

As you can see, the chart instance generated by Highcharts is stored on the HighchartsComponent instance. But what if you want to access the chart (and all the methods that allow you to manipulate the chart) outside of this component? Let’s say, for example, we want to create a button that when clicked upon, uses the built in Highcharts API to destroy the chart.

This is where refs can come in handy.

const highchartsOptions = {

// ...highcharts options

}; class Container extends React.Component {

constructor() {

super();

this.handleButtonClick = this.handleButtonClick.bind(this);

} handleButtonClick() {

if (this.highchartsComponent.chart) {

this.highchartsComponent.chart.destroy();

}

}



render() {

return (

<div>

<HighchartsComponent

container="chart"

options={highchartsOptions}

ref={highchartsComponent =>

this.highchartsComponent = highchartsComponent

}

/>

<button onClick={this.handleButtonClick}>

Destroy Chart

</button>

</div>

)

}

}

In this snippet, the ref on HighchartsComponent allows me to store a reference to that mounted component instance on the parent Container component. This is how I’m able to access its chart property and leverage the Highcharts API by calling the destroy() method inside of the button click handler.

Using refs on connected components

Okay… So what about components that are connected to a Redux store via the React-Redux connect() API? Should be simple enough, right?

Let’s go ahead and connect our HighchartsComponent from the previous example to an imaginary Redux store.

connect()(HighchartsComponent);

And… Everything just broke. 😭

The reason this no longer works is that connect() is a Higher Order Component that takes in whatever component you feed it and returns an entirely new component. So the ref on HighchartsComponent now points to the connected component instance instead of the HighchartsComponent instance. And if we try to access the chart property, it will naturally come back undefined.

So what can we do? Fortunately, the React-Redux API provides a way of getting at the wrapped component instance (in this case, our HighchartsComponent). All you have to do is set the withRef option to true inside of your connect() call:

connect(null, null, null, { withRef: true })(HighchartsComponent);

Then, you can use the getWrappedInstance() method to… you guessed it… get the wrapped instance. So now, our previous example looks like this:

class Container extends React.Component {

constructor() {

super();

this.handleButtonClick = this.handleButtonClick.bind(this);

} handleButtonClick() {

if (this.highchartsComponent.chart) {

this.highchartsComponent.chart.destroy();

}

}



render() {

return (

<div>

<HighchartsComponent

container="chart"

options={chartOption}

ref={connectedComponent =>

this.highchartsComponent =

connectedComponent.getWrappedInstance();

}

/>

<button onClick={this.handleButtonClick}>

Destroy Chart

</button>

</div>

)

}

}

And everything works! 🎉

Update: As of react-redux >= v6.0, withRef and getWrappedInstance are deprecated in favor of the forwardRef option, which simplifies things as you no longer need to call getWrappedInstance to get at the wrapped component instance.

For the full code, check out the JSfiddle.