1. ホーム
  2. React

【React】useDebugValue の使い方|カスタムフックを React DevTools で見やすくする

Share

自作のカスタムフックが増えてくると、React DevTools の Hooks パネルに StateRef といった内部の値がそのまま並び、どのフックが今どんな状態なのか一目で分からなくなることがあります。そんなときに役立つのが useDebugValue です。この記事では、useDebugValue がどんなフックで、React DevTools にどう表示されるのか、基本構文から useOnlineStatususeMediaQuery といったカスタムフックでの使い方、第2引数のフォーマット関数で整形を遅延させるテクニック、そして「使うべき場面・使わなくてよい場面」までを、動く TSX のコードで解説します。React 公式の仕様に沿って正確にまとめます。

useDebugValue は「カスタムフックのラベル」を DevTools に出すフック

useDebugValue は、カスタムフックの中で呼び出し、そのフックに読みやすいラベル(値)を付けて React DevTools に表示させるためのフックです。アプリの動作そのものには一切影響しません。表示されるのは開発者向けのデバッグ用ラベルだけで、本番のユーザーには見えませんし、レンダリング結果も変わりません。あくまで「開発時に DevTools 上でフックの状態を見やすくする」ことだけを目的としたフックです。

基本の構文は次のとおりです。第1引数に表示したい値を渡すだけで、戻り値はありません(何も返しません)。

構文
useDebugValue(value)

// 第2引数にフォーマット関数を渡すこともできる
useDebugValue(value, format?)

ここで value は DevTools に表示したい値で、型は何でも構いません(文字列でも数値でもオブジェクトでもよい)。format は省略可能なフォーマット関数で、その役割は後半で詳しく説明します。1つ注意したいのは、useDebugValue はコンポーネントの中ではなく、カスタムフックの中で呼び出すという点です。React 公式ドキュメントでも「カスタムフックの最上位(top level)で呼ぶ」ことが前提とされています。

useOnlineStatus フックにラベルを付けてみる

まずは典型的な例として、オンライン/オフラインの状態を返す useOnlineStatus というカスタムフックを見てみます。useSyncExternalStore でブラウザのオンライン状態を購読し、真偽値を返すフックです。ここに useDebugValue を加えると、DevTools 上で状態が読みやすくなります。

useOnlineStatus.ts
import { useSyncExternalStore, useDebugValue } from 'react';

function subscribe(callback: () => void) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

export function useOnlineStatus() {
  const isOnline = useSyncExternalStore(
    subscribe,
    () => navigator.onLine, // クライアントでの値
    () => true              // サーバーでの値
  );

  // DevTools に "Online" / "Offline" と表示する
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

useDebugValue を付けない場合、DevTools の Hooks パネルにはフックの中身(内部で使っている SyncExternalStore の値)が並ぶだけで、そのフックが「今オンラインなのかオフラインなのか」はぱっと見では分かりません。useDebugValue(isOnline ? 'Online' : 'Offline') と書いておくと、このフックを使っているコンポーネントを DevTools で選んだとき、OnlineStatus: "Online" のようにフック名の横にラベルが表示されますuse を除いたフック名(この例では OnlineStatus)がラベルの見出しになります。

このフックを実際に画面で使うと、次のようになります。

StatusBar.tsx
import { useOnlineStatus } from './useOnlineStatus';

export function StatusBar() {
  const isOnline = useOnlineStatus();
  return <p>{isOnline ? '✅ オンライン' : '❌ オフライン'}</p>;
}

コンポーネント側のコードは useDebugValue を意識する必要がありません。ラベルの表示はカスタムフックの内部で完結しているためです。

useMediaQuery でも状態をラベル表示する

もう1つ、メディアクエリの一致状態を返す useMediaQuery でも同じ考え方が使えます。画面幅の条件に一致しているかどうかを真偽値で返すフックに、現在の一致状態をラベルとして付けてみます。

useMediaQuery.ts
import { useSyncExternalStore, useDebugValue } from 'react';

export function useMediaQuery(query: string) {
  const matches = useSyncExternalStore(
    (callback) => {
      const mql = window.matchMedia(query);
      mql.addEventListener('change', callback);
      return () => mql.removeEventListener('change', callback);
    },
    () => window.matchMedia(query).matches, // クライアントでの値
    () => false                             // サーバーでの値
  );

  // 例: "(min-width: 768px) → matched" のように表示する
  useDebugValue(`${query} → ${matches ? 'matched' : 'not matched'}`);

  return matches;
}

このように、useDebugValue に渡す値は真偽値そのままでもよいですが、"matched" / "not matched" のように人間が読んで意味の分かる文字列に変換して渡すと、DevTools 上でぐっと見やすくなります。フックが複数のコンポーネントで使われているとき、どのインスタンスがどんな状態かを DevTools 上で見分けやすくなるのが利点です。

第2引数のフォーマット関数で整形を遅延させる

useDebugValue の第2引数には、フォーマット関数を渡せます。この関数は第1引数の値を受け取り、表示用に整形した値を返します。ポイントは、このフォーマット関数が呼ばれるのは、実際に DevTools でそのコンポーネントを開いて中を覗いたときだけだという点です。つまり、整形処理が毎レンダーで走ることはありません。

これは、表示用の整形にコストがかかる場合に効いてきます。たとえば Date オブジェクトを読みやすい文字列にするのに toDateString() のような変換を毎回行うと、DevTools を開いていないときにも無駄な処理が発生してしまいます。フォーマット関数に切り出しておけば、DevTools で検査したときだけ変換が実行されます。

useLastUpdated.ts
import { useDebugValue } from 'react';

export function useLastUpdated(date: Date) {
  // 第2引数の関数は、DevTools で検査したときだけ呼ばれる。
  // そのため toDateString() は毎レンダーでは実行されない。
  useDebugValue(date, (d) => d.toDateString());

  return date;
}

フォーマット関数は、第1引数に渡したデバッグ値(この例では date)を引数として受け取り、表示したい値を返します。返す値の型も何でも構いません。もしフォーマット関数を渡さなければ、第1引数の値がそのまま DevTools に表示されます。整形のコストが軽い(単純な文字列連結など)なら第2引数は不要で、コストのかかる変換をするときにだけ第2引数を使う、という判断で問題ありません。

どんなときに使い、どんなときは使わなくてよいか

useDebugValue は便利ですが、React 公式ドキュメントはすべてのカスタムフックに付けることは推奨していません。付ける価値が高い場面とそうでない場面を整理しておきます。

場面useDebugValue を使うか
共有ライブラリとして配布するカスタムフック使う価値が高い
内部データ構造が複雑で、DevTools では中身を追いにくいフック使う価値が高い
値をそのまま返すだけの単純なフック基本は不要
DevTools で内部の値がすでに読みやすく表示されるフック基本は不要

公式が特に勧めているのは、共有ライブラリの一部で、内部データ構造が複雑で検査しづらいカスタムフックです。こうしたフックは利用者が中身を把握しにくいため、要約されたラベルを出しておくとデバッグの助けになります。逆に、アプリ内で完結する単純なフックや、内部の useState の値を見れば状態が分かるようなフックには、わざわざ付ける必要はありません。DevTools のノイズを増やすだけになりがちです。

なお、useDebugValue はコンポーネントの本体に直接書くものではなく、あくまでカスタムフックの中で使うフックです。コンポーネントの状態はもともと DevTools でそのまま見えるため、フック以外の場所で使う意味はありません。

まとめ

useDebugValue は、カスタムフックの中で呼び出し、React DevTools にそのフックの状態を表すラベルを表示させるためのフックです。基本構文は useDebugValue(value) で戻り値はなく、アプリの動作には影響しません。useOnlineStatususeMediaQuery のように、真偽値を "Online" / "Offline" のような読みやすい文字列に変換して渡すと、DevTools 上でフックの状態が一目で分かります。第2引数にフォーマット関数を渡すと、その整形は DevTools で検査したときだけ実行されるため、toDateString() のようなコストのかかる変換を毎レンダーで走らせずに済みます。ただし全フックに付ける必要はなく、公式が勧めるのは共有ライブラリ向けの、内部構造が複雑なカスタムフックです。開発体験を上げるための道具として、必要な場面で活用してみてください。

参考ページ