95
59

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 3 years have passed since last update.

React + TypeScript: useRefの3つの型指定と初期値の使い方

Last updated at Posted at 2021-03-18

ReactとTypeScriptの使い方がまとめられた「React + TypeScript Cheatsheets」の「useRef」の情報にもとづいて、このフックの型づけと初期値の与え方について3つの定めを解説します。useRefフックの型には、試しやすいようにnumberを用いました。

const nullRef = useRef<number>(null);
const nonNullRef = useRef<number>(null!);
const nullableRef = useRef<number | null>(null);

nullで初期化したとき

useRefフックの初期値にnullを与えると、戻り値のrefオブジェクトは読み取り専用です。つまり、currentプロパティは書き替えられません。

// 読み取り専用。
const nullRef = useRef<number>(null);
// 読み取り専用プロパティであるため、'current' に代入することはできません。
// nullRef.current = 1;

読み取り専用にする典型は、useRefをHTML要素(HTMLElement)で型づけし、要素のref属性に与える場合です(「useRef」参照)。このとき、currentプロパティは書き替えないでしょう。

たとえば、つぎのようにuseRefnullを渡してHTMLDivElementで型づけると、そのcurrentプロパティ(div)はHTMLDivElement | nullのユニオン型で推論されます。すると、要素の参照として扱う前に、必ずnullでないことを確かめなければなりません。そうしなければ、つぎのようなエラーが示されてしまうのです。

オブジェクトは 'null' である可能性があります。

function App() {
	const divRef = useRef<HTMLDivElement>(null);
	useEffect(() => {
		const div = divRef.current;
		if (!div) throw Error("divRef is not assigned");  // nullでないことを確かめる
		console.log(div.clientWidth);
	}, []);
	return (
		<div ref={divRef} />
	);
}

useRefの引数に定めるHTML要素の型は、refを与える要素に応じて適切に絞り込んでください。あえて広めの型を定めると、エラーメッセージから適切な型が示されることもあります。

function App() {
	// const inputRef = useRef<HTMLInputElement>(null);  // ←正しくは
	const inputRef = useRef<HTMLElement>(null);
	return (
		/* 型 'RefObject<HTMLElement>' を型 'LegacyRef<HTMLInputElement> | undefined' に割り当てることはできません。
		型 'RefObject<HTMLElement>' を型 'RefObject<HTMLInputElement>' に割り当てることはできません。
		型 'HTMLElement' には 型 'HTMLInputElement' からの次のプロパティがありません: accept, align, alt, autocomplete、49 など。 */
		<input ref={inputRef} type="text" />
	);
}

初期値nullに非nullアサーション演算子!を添える

初期値のnull非nullアサーション演算子(non-null assertion operator)!を添えてnull!とすると、今度はrefオブジェクトのcurrentプロパティが書き替えられます。もちろん、代入できるのは指定した型のみで、nullは入れられません。

// 指定した型の値で書き替えられる。nullは不可。
const nonNullRef = useRef<number>(null!);
console.log(nonNullRef.current);  // null
// 型 'null' を型 'number' に割り当てることはできません。
// nonNullRef.current = null;
nullableRef.current = 1;
console.log(nullableRef.current);  // 1

nullでないことを型アサーションすれば、要素のrefに定めたときも、参照する前にnullかどうか確かめる必要がありません。ただし、nullでないことは、コードを書く側に責任が委ねられます。たとえば、要素にrefが与えられていないなど、参照が正しく得られなければ、ランタイムエラーになるでしょう。

function App() {
	const divRef = useRef<HTMLDivElement>(null!);
	useEffect(() => {
		const div = divRef.current;
		// nullでないことは確かめなくてよい
		console.log(div.clientWidth);
	}, []);
	return (
		<div ref={divRef} />
	);
}

useRefフックの役割は、refオブジェクトを要素のref属性に与えることだけではありません(「React: useRefフックで状態変数のひとつ前の値や最新の値を得る」参照)。初期値はnullとしつつ、currentプロパティを書き替えたい場合に用います。

型指定にnullを含める

useRefフックに書き替え可能の初期値nullを渡したうえで、あえてnullも代入値に含めたいときは、型指定にユニオン型(union type)|で加えます。

// 型指定にnullを含める。
const nullableRef = useRef<number | null>(null);
nullableRef.current = 1;
console.log(nullableRef.current);  // 1
nullableRef.current = null;
console.log(nullableRef.current);  // null

まとめ

useRefの3つの型指定と初期値の与え方で、currentプロパティの扱いが少しずつ違ってくるということです。それらを意識して、用途に応じて使いましょう。

// 読み取り専用。
const nullRef = useRef<number>(null);
// 指定した型の値で書き替えられる。nullは不可。
const nonNullRef = useRef<number>(null!);
// 型指定にnullを含める。
const nullableRef = useRef<number | null>(null);
95
59
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
95
59

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?