Strongly Typed React Refs with TypeScript

November 2, 2016 3 minutes read

If you’ve been working with React for some time you probably figured out that refs are very easy to start with, but rather inflexible after some point.

But anyway, refs are available and they work. So why not use them?

When working with TypeScript it’s usually a common practice to have everything as strongly typed as possible.

Unfortunately refs is typed as [key: string]: ReactInstance, which force us to type this.refs["myInput]. This is definitely not what we want.

Using callback references

ref as a callback is the recommended approach on how to use it nowadays, and the solution with TypeScript is pretty simple.

import * as React from "react";

export class CustomTextInput extends React.Component<{}, {}> {
  private textInput: HTMLInputElement;

  constructor() {
    super();
    this.focus = this.focus.bind(this);
  }

  public focus() {
    this.textInput.focus();
  }

  public render() {
    return <div>
              <input
                type="text"
                ref={(ref) => this.textInput = ref}
              />
              <input
                type="button"
                value="Focus the text input"
                onClick={this.focus}
              />
           </div>;
  }
}


export class AutoFocusTextInput extends React.Component<{}, {}> {
  private input: CustomTextInput;

  constructor() {
    super();
  }

  protected componentDidMount() {
    this.input.focus();
  }

  public render() {
    return <div>
              <CustomTextInput
                ref={(ref) => this.input = ref}
              />
           </div>;
  }
}

This example is exactly the same as in the official ref docs. The only single difference is that we have add a typed property to our class private textInput: HTMLInputElement;. With this is now possible to safely type this.input.value, this.input.focus(), this.input.maxLength and so on, without compiler warnings.

Using string references

The string refs are eventually going to be deprecated. But in case you’re still using it and don’t want to migrate to callback references, it’s also pretty simple to add typings to it.

import * as React from "react";

export class CustomTextInput extends React.Component<{}, {}> {
  public refs: {
    textInput: HTMLInputElement;
  };

  constructor() {
    super();
    this.focus = this.focus.bind(this);
  }

  public focus() {
    this.refs.textInput.focus();
  }

  public render() {
    return <div>
              <input
                type="text"
                ref="textInput"
              />
              <input
                type="button"
                value="Focus the text input"
                onClick={this.focus}
              />
           </div>;
  }
}

We’re simply overriding the refs property to be strongly typed. Yeah, that’s all.

The difference between this example and the above is that this one is using the standard React refs property while the other is defining a new one.

Whatever you prefer, just don’t forget to “type” your React components as much as possible.