A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 below:

React Refs with TypeScript. All your questions related to… | by Martin Hochel

Recently I’ve got this message on Twitter

Instead of replying, I’ve decided to write down this “short post” about how to handle React DOM refs and ref forwarding with TypeScript for anyone new to React and TypeScript as I didn’t found this resource anywhere online.

Disclaimer:

Don’t expect to learn all the why’s and how’s about React refs within this blogpost ( you can find all that info in excellent React docs).

This post will try to mirror those docs a bit for easy context switching.

What are Refs in React ?

Refs provide a way to access DOM nodes or React elements created in the render method.

Let’s create some React refs with TypeScript 👌👀

Creating Refs
class MyComponent extends Component {
// we create ref on component instance
private myRef = createRef()
render() {
return <div ref={this.myRef} />
}
}

But with that component definition, we get compile error:

[ts]
Type 'RefObject<{}>' is not assignable to type 'RefObject<HTMLDivElement>'.

Uh oh? What’s going on here ? TypeScript has very good type inference especially within JSX, and because we are using ref on an <div> element, it knows that the ref needs to be a type of HTMLDivElement. So how to fix this error?

React.createRef() is an generic function

// react.d.ts
function createRef<T>(): RefObject<T>

What about the return type, the RefObject<T> ? Well that's just a Maybe type with following interface

// react.d.ts
interface RefObject<T> {
// immutable
readonly current: T | null
}

With that covered, you already know how to make our code valid right ? 👀 We need to explicitly set the generic value for createRef:

Press enter or click to view image in full size createRef with TypeScript Accessing refs

When a ref is passed to an element in render, a reference to the node becomes accessible at the currentattribute of the ref.

If we wanna access our previously defined ref value, all we need to do is to get the current value from the ref object

const node = this.myRef.current

Although, our node variable is gonna be a Maybe type 👉 HTMLDivElement OR null ( remember? RefObject<T>interface... ).

So if we would like to execute some imperative manipulation with that node we just couldn't do:

class MyComponent extends Component {
private myRef = React.createRef<HTMLDivElement>()
focus() {
const node = this.myRef.current
node.focus()
}
}

with that code, we would get an compile error

[ts] Object is possibly 'null'.
const node: HTMLDivElement | null

You may think right now: “this is annoying, TypeScript sucks…” Not so fast partner 😎! TypeScripts just prevents you to do a programmatic mistake here which would lead to runtime error, even without reading a line of the docs ❤️.

What React docs say about current value ?

React will assign the current property with the DOM element when the component mounts, and assign it back to null when it unmounts. ref updates happen before componentDidMount or componentDidUpdate lifecycle hooks.

That’s exactly what TS told you by that compile error! So to fix this, you need to add some safety net, an if statement is very appropriate here ( it will prevent runtime errors and also narrow type definition by removing null ):

Press enter or click to view image in full size type narrowing with runtime guard

Also we get autocomplete to whole HTMLDivElement DOM api. Lovely!

type narrowing and intellisense in action

Curious reader may ask:

What about accessing refs within componentDidMount if we don't wanna encapsulate our imperative logic within a method ( because we are messy/bad programmers 😇 ) ?

I hear you… Because we know that our refs current value is definitely gonna be available within componentDidMount, we can use TypeScript's Non-null assertion operator 👉 👉 👉 !

Press enter or click to view image in full size using ! operator for ref type narrowing ( I know what I’m doing trust me 👀 )

That’s it!

But hey, I really recommend encapsulating the logic to separate method with descriptive method name. Ya know readable code without comments and stuff 🖖 …

Adding a Ref to a Class Component

If we wanted to wrap our MyComponent above to simulate it being focused immediately after mounting, we could use a ref to get access to the MyComponent instance and call its focus method manually:

import React, { createRef, Component } from 'react'class AutoFocusTextInput extends Component {
// create ref with explicit generic parameter
// this time instance of MyComponent
private myCmp = createRef<MyComponent>()
componentDidMount() {
// @FIXME
// non null assertion used, extract this logic to method!
this.textInput.current!.focus()
}
render() {
return <MyComponent ref={this.textInput} />
}
}

Note I: this only works if MyComponent is declared as a class

Note II: we get access to all instance methods as well + top notch DX thanks to TypeScript

Beautiful isn’t it ? 🔥

Refs and Functional Components

You may not use the ref attribute on functional components because they don’t have instances

You can, however, use the ref attribute inside a functional component as long as you refer to a DOM element or a class component:


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4