9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【React】TypeScriptでuseRefを使う際の型の使い分け

Posted at

はじめに

以前useRefの2つの使い方について記事にまとめたが、useRefTypeScriptのもとで使う際には、2つの使い方それぞれで必要な型定義が異なることを学んだので、今回は型の使い分けについてまとめる。

useRefの2つの使い方

まずはuseRefの2つの使い方を簡単におさらいする。

使用方法① : DOMへのアクセス

  • 例えば のinput要素にフォーカスしたい場合、JavaScriptdocument.getElementById('name').focus() というように書くが、これと同じことをReactで行いたい場合にuseRefを使ってDOMへアクセスすることができる。
  • hooks誕生以前からのrefの使い方。

使用方法② : 値を保持する

  • useStateも値を保持することが出来るが、useStateと違いuseRefでは値を更新してもコンポーネントの再レンダリングが起きないというのが特徴。
  • hooks誕生により新たにできたrefの使い方。

useRefの返り値

では2種類の使い方を確認したところで型の話に入るが、実はuseRefの返り値にも RefObjectもしくはMutableRefObject という2種類の型がある。

RefObject

  • hooks登場以前からのRefObject。
  • .currentの初期値はnullであり、.currentに readonly の制約が付いている。

MutableRefObject

  • hooksによりできた値を保持するという用例に対応するため、 readonly の制約がないMutableRefObjectが設けられた。

返り値の型の使い分け

上記の特徴を見ると察することができる通り、DOMへのアクセスの為(従来からの用例)に使う場合は RefObject 型を使い、値の保持の為(hooks以後の用例)に使う場合は readonly 制約のない MutableRefObject 型を使う。

そしてこれら返り値の型は初期値と引数の与え方によって決まるので、使用方法に合わせた初期値と引数を与える必要がある。

DOMへのアクセスに使う場合の型付け

DOMへのアクセスの為にuseRefを使う場合は、返り値を RefObject 型にする必要がある。

どうすれば RefObject 型になるのかというと、初期値がnull かつ 型定義にnullを含まない ようにすると、返り値は RefObject型となる。

 
よってDOMへのアクセスに使う場合は、以下のようにする。

  • 初期値はnullを与える
  • 型引数は、中身に入りうるDOMノードもしくはReact要素を与える
function Home() {
  const ref = useRef<HTMLInputElement>(null)
  return <input ref={ref}>Home</input>
}

仮に上記の条件を満たしておらず返り値が MutableRefObject型となった場合、ref属性に設定する箇所でTypeScriptのエラーが出る。

function Home() {
  const ref = useRef<HTMLInputElement>()    // MutableRefObject型になる
  return <input ref={ref}>Home</input>      // エラー
}

Untitled.png

値の保持に使う場合の型付け

値の保持に使う場合のは返り値を MutableRefObject型にする必要があるが、上記の RefObject型の条件に当てはまらない場合は、全てMutableRefObject型となる。

なので通常のTypeScriptのセオリー通り、なるべく初期値を与え、型引数は初期値からの推論に任せるか明示的に書くと良い。

// 推論されて `MutableRefObject<number>` になる。
const ref = useRef(0)

// `MutableRefObject<string | null>` になる。
const ref = useRef<string | null>(null)

仮に RefObject型なのにcurrentプロパティに値を代入しようとすると、「読み取り専用プロパティであるため、'current' に代入することはできません。」というエラーが出る。

さいごに

TypeScriptを使っていると、思わぬところで躓き時間がかかることがある。

今回は返り値の型の違いを知ったことでuseRefについての理解も深まったので、今後もその場しのぎでTypeScrptのエラーが消えればOKではなくしっかり理解してTypeScriptを活用していきたい。

参考記事

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?