1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「クリックしても開かない」ReactのUIバグを処理経路から切り分けた話

1
Last updated at Posted at 2026-09-21

はじめに

ReactでUIを実装していると、次のような不具合に遭遇することがあります。

  • メニューを開くボタンは表示されている
  • クリックしてもメニューが開かない
  • 常に発生するわけではなく、特定の条件でのみ発生する

このような症状を見ると、

  • クリックイベントが発火していない?
  • 透明な要素が重なっている?
  • z-index の問題?
  • メニューが画面外に描画されている?

など、クリック周辺の原因を考えたくなります。

もちろん、原因の候補として仮説を立てることは重要です。

しかし、「クリックしても開かない」という症状でも、原因がクリック処理そのものにあるとは限りません。

このような不具合を切り分けるときに重要なのは、

症状から原因を推測し続けるのではなく、実際の処理経路を一つずつ確認し、「どこまで正常で、どこから期待とズレたのか」を探すこと

です。

この記事では、説明用のサンプルを使いながら、ReactのUI不具合を処理経路から切り分ける方法を整理します。

※掲載しているコード、データ構造、名称は説明用に単純化したサンプルです。実際のシステム固有の実装や仕様を示すものではありません。

まず「クリックできているか」を確認する

「ボタンを押しても何も起きない」場合、まずクリック処理まで到達しているかを確認します。

例えば、次のような処理を考えます。

const handleClickMenu = (item: Item) => {
  setTargetItem(item);
};

イベントハンドラにブレークポイントを設定するなどして、クリック時に関数へ到達しているか確認します。

ボタンをクリック
      ↓
onClick
      ↓
handleClickMenu()

ここまで到達していることが確認できれば、少なくともイベント発火までは正常です。

つまり、

  • onClick が呼ばれていない
  • クリックイベントが要素まで届いていない

といった問題は、直接の原因ではない可能性が高くなります。

ここでクリック周辺だけを調べ続けるのではなく、次の処理へ進みます。

次に「クリック後に何が起きるか」を追う

クリックイベントまで正常であれば、その後の処理を確認します。

例えば、

setTargetItem(item);

によって、対象となるデータをstateへ保存する実装を考えます。

処理経路は、

クリック
   ↓
state更新
   ↓
?
   ↓
メニュー表示

となります。

次に確認したいのは、この ? の部分です。

例えば、stateに保存されたデータをもとに、表示可能なメニュー項目を生成しているとします。

const menuItems = [];

if (actions.rename.available) {
  menuItems.push({ label: "名前を変更" });
}

if (actions.duplicate.available) {
  menuItems.push({ label: "複製" });
}

if (actions.archive.available) {
  menuItems.push({ label: "アーカイブ" });
}

ある条件で、この menuItems が空になるケースを考えてみます。

すると処理としては、

クリック成功
    ↓
state更新成功
    ↓
表示するメニュー項目が0件

となります。

この場合、「クリックしても何も起きない」のではありません。

クリック後の処理自体は進んでいることが分かります。

「何をもってメニューを開くのか」を確認する

さらに処理を追います。

例えば、メニューの開閉が次の条件で決まっているとします。

const isMenuOpen =
  targetItem !== null &&
  menuItems.length > 0;

クリックによって targetItem が設定されていても、

menuItems.length === 0

であれば、

true && false; // false

となり、メニューは開きません。

つまり、

「クリックに反応していない」のではなく、「クリック後の処理は正常に進んでいるが、最終的な表示条件を満たしていない」

という可能性が見えてきます。

ここまで確認できれば、調査対象をクリックイベントやCSSから、メニュー項目の生成条件や表示条件へ移せます。

では、なぜボタンは表示されているのか

ここで、さらに上流へ戻って考えてみます。

メニュー項目が0件になるのであれば、

なぜメニューを開くボタン自体は表示されているのか?

という疑問が生まれます。

例えば、ボタンの表示条件が次のようになっているとします。

const shouldShowMenuButton = Object.values(actions).some(
  (action) => action.available,
);

一方、actions にメニューで使用する操作だけでなく、別のUIから実行する操作も含まれているとします。

const actions = {
  rename: { available: false },
  duplicate: { available: false },
  archive: { available: false },
  bookmark: { available: true },
};

ここでは、bookmark はメニューではなく、独立したUIから実行する操作だとします。

しかし、

Object.values(actions).some(
  (action) => action.available,
);

では、bookmark も判定対象になります。

その結果、

メニュー外の操作だけが利用可能
        ↓
「利用可能な操作がある」と判定
        ↓
メニューボタンを表示
        ↓
クリック
        ↓
state更新
        ↓
メニュー項目を生成
        ↓
メニュー項目は0件
        ↓
メニューを開かない

という状態が発生します。

このように処理経路を整理すると、

ボタンは表示されるのに、クリックしてもメニューが開かない

という現象を説明できます。

「原因候補を考える」と「原因を確認する」は違う

最初に、

CSSかもしれない
イベントの問題かもしれない

と仮説を持つこと自体は問題ありません。

ただし、画面上の症状だけから原因を決めることはできません。

例えば、

クリック処理に到達したか
        ↓
stateは更新されたか
        ↓
表示するデータは生成されたか
        ↓
最終的な表示条件を満たしたか

と実際の処理経路を確認すれば、一つずつ可能性を切り分けられます。

「何が怪しそうか」を考えることと、「実際にどこまで正常に動いているか」を確認することは別です。

仮説を立てた後に、コードや実行結果によって検証することが重要です。

症状ではなく「最初に期待とズレた場所」まで戻る

画面上の症状が、

ボタンをクリックしてもメニューが開かない

だったとします。

このとき、メニューの開閉条件だけを変更して、

const isMenuOpen = targetItem !== null;

としてしまえば、中身が空のメニューが開く可能性があります。

それでは、目に見える症状を別の形に変えただけです。

処理を前から並べると、

ボタン表示判定
      ↓
クリック
      ↓
state更新
      ↓
メニュー項目生成
      ↓
開閉判定

となります。

このケースで最初に期待とズレているのは、最後の開閉判定ではありません。

メニュー項目が存在しない条件でも、メニューボタンを表示できてしまうことです。

つまり、問題が画面に現れた場所よりも、原因となる判断が上流に存在することがあります。

不具合を修正するときは、

どの条件を変更すれば症状が消えるか

だけでなく、

どこで最初に期待と実際がズレたのか

まで戻って確認することが重要です。

UIの不具合を処理経路から切り分ける

同様のUI不具合では、次のような順序で確認すると原因を切り分けやすくなります。

1. イベントは発火しているか

  • onClick などのイベントハンドラまで到達しているか

2. stateは期待どおり更新されているか

  • 操作後に必要な値がセットされているか

3. 表示に必要なデータは生成されているか

  • 配列が空になっていないか
  • 条件によって必要なデータが除外されていないか

4. 最終的な表示条件を満たしているか

  • isOpen などの値が、どの条件から決まっているか

5. さらに上流で、なぜその状態になったのか

  • 最終的な条件だけを変更するのではなく、どこで最初に期待と実際がズレたのか

「クリックしても開かない」という症状でも、原因がクリック処理にあるとは限りません。

イベント
  ↓
state
  ↓
データ生成
  ↓
表示条件

と処理経路を順番に確認し、

「どこまで正常に動いていて、どこから期待とズレたのか」

という境界を探すことで、調査範囲を絞りやすくなります。

まとめ

「クリックしても開かない」という症状だけを見ると、クリックイベントやCSSを疑いたくなります。

しかし、症状が現れている場所と、最初に期待とズレた場所が同じとは限りません。

UIの不具合を調査するときは、

イベント
  ↓
state
  ↓
データ生成
  ↓
表示条件

のように処理経路を順番に確認し、

「どこまで正常に動いていて、どこから期待とズレたのか」

という境界を探すことで、原因を切り分けやすくなります。

さらに、最終的な条件だけを変更するのではなく、

最初に期待と実際がズレた判断まで戻る

ことを意識すれば、目に見える症状だけを隠す修正も避けやすくなります。

症状から原因を決めつけるのではなく、

どの入力が、どの処理を通り、どこで初めて期待値と実際の値がズレたのか

を確認することが、不具合調査では重要だと考えています。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?