Author avatar

Chris Dobson

Using React Refs in Typescript

Chris Dobson

  • Aug 7, 2019
  • 7 Min read
  • 149 Views
  • Aug 7, 2019
  • 7 Min read
  • 149 Views
Web Development
React

Introduction

For the vast majority of React components, everything can be done using the declarative model which communicates with child components using props to re-render. On some occasions, however, it is necessary to use an imperative programming model and access an underlying DOM element. Examples of when this is necessary could be to set the focus of an element, access the actual dimensions of an element, or to use a non-React 3rd party library that only operates on DOM elements. Whatever the reason this can be accomplished using React refs.

This guide will show how to use strongly typed refs using Typescript. We will see how to use refs from functional components, using the hooks API, from class components in version 16.3 and above, from class components in versions prior to 16.3 and forwarding refs to other components. Each example will render a div with a width of 100%, get a ref to the div element, and write the actual width of it to the console using the clientWidth property.

Please note that, regardless of the reasons for using refs, they should be used sparingly and the declarative model should be used whenever possible.

Functional Components

To use refs in a functional component, we create the ref using the useRef hook:

1
const divRef = React.useRef<HTMLDivElement>(null);
typescript

This code creates an instance of a RefObject that can take a ref of type HTMLDivElement; the RefObject has a single property, current, that can be set to either null or an HTMLDivElement instance. As we have yet to render the component, the ref is initialized to null.

The ref is set when the element is declared using the ref prop:

1
return <div ref={divRef} style={{ width: "100%" }} />;
typescript

If a different element than div is used, the Typescript compiler will throw an error and divRef.current will then be set to the underlying DOM element of the div and can be accessed in the component code. For this example we shall use the ref to write the clientWidth to the console when the component has mounted:

1
2
3
4
5
React.useEffect(() => {
  if (divRef.current) {
    console.log(`hookRef div width: ${divRef.current.clientWidth}`);
  }
}, []);
typescript

This code uses the Effect hook with an empty array as the dependency parameter to ensure the code is only executed when the component is mounted. It simply tests that the ref has been set - the Typescript compiler will report an error if the if statement is omitted - and, if it has, writes the width of it to the console.

The code for this component can be found here.

Class Components

When using class components, refs are still available. In a class it can be created as a class field:

1
divRef = React.createRef<HTMLDivElement>();
typescript

As in the functional component example above, this sets this.divRef to a RefObject that takes a ref of type HTMLDivElement. In this case, the ref is initialized to null without having to specify any parameters.

In the render method, the ref prop is used in the same way as the previous example:

1
2
3
render() {
  return <div ref={this.divRef} style={{ width: "100%" }} />;
}
typescript

The DOM element can now be accessed from within the class using this.divRef.current, so it can be used when the component has mounted like this:

1
2
3
4
5
componentDidMount() {
  if (this.divRef.current) {
    console.log(`createRefRef div width: ${this.divRef.current.clientWidth}`);
  }
}
typescript

As in the previous example, the code tests that the ref has been set and writes the width to the console.

The code for this component can be found here.

Ref Callback

The createRef function was introduced in React version 16.3. So, when using a previous version, a different technique needs to be used to access a ref. In the previous examples, the ref prop has been set to an instance of RefObject but it can also be set to a callback method that accepts a parameter of the DOM element. This callback can then be used to set a class field to the element:

1
2
3
4
5
divRef: HTMLDivElement | null = null;

setDivRef = (element: HTMLDivElement) => {
  this.divRef = element;
};
typescript

As there is no RefObject, the class field is typed as HTMLDivElement | null, meaning that it can be set to either null or an HTMLDivElement and is initialized to null.

Then in the render method the ref prop is set to this callback:

1
2
3
render() {
  return <div ref={this.setDivRef} style={{ width: "100%" }} />;
}
typescript

Finally in componentDidMount the ref can be accessed:

1
2
3
4
5
componentDidMount() {
  if (this.divRef) {
    console.log(`callbackRef div width: ${this.divRef.clientWidth}`);
  }
}
typescript

The code for this component can be found here.

Forwarding Refs

Sometimes it can be useful to access a DOM element inside a child React component from within the parent. To do this, we can use forwarding refs.

To accept a forward ref the child component is created using the forwardRef function which accepts two parameters - props and ref. The ref can then be set when the element is declared in the same way, as if it had been created within the component:

1
2
3
const Forward = React.forwardRef((props, ref: React.Ref<HTMLDivElement>) => (
  <div ref={ref} style={{ width: "100%" }} />
));
typescript

Rather than, as in previous examples, being of type RefObject, the forwarded ref is of type Ref.

Accessing the forwarded ref is exactly the same as using a regular ref - create the ref, set the ref prop, and access it using the current property:

1
2
3
4
5
6
7
8
9
10
11
function ForwardRefRef() {
  const divRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    if (divRef.current) {
      console.log(`forwardRefRef div width: ${divRef.current.clientWidth}`);
    }
  });

  return <Forward ref={divRef} />;
}
typescript

The code for this component can be found here.

Conclusion

This guide has shown various techniques for using refs in Typescript in different types of React components.

The code for each component can be found here - functional components, class components, callback refs, and forwarding refs.

2