TT Lab
はじめる
学ぶ 学習パス コース

クジラを探したのに、猫が現れた

クジラを探したのに、猫が現れた

TT Labで続きを見る

一言でいうと

検索ボックスの入力、サーバーに送ったリクエスト、画面に表示した結果は、それぞれ異なる時点の情報です。3つを1つの文字列だと考えると、遅れて届いた過去のレスポンスが最新の画面を占領します。

なぜ必要なのか

月面基地の図書館で、猫を検索してすぐにクジラへ切り替えました。観測所2か所にリクエストが飛び、クジラのレスポンスが先に届きました。画面にクジラが表示された直後、長く待たされた猫のレスポンスが到着します。エラーのダイアログもなく、HTTPレスポンスも成功なのに、画面には間違った情報が残ります。これは検索アルゴリズムよりも、今、誰が画面を書き換える資格を持っているのかという問題です。

ここで検索ボタンを無効にすれば、現象は隠せます。しかし、ユーザーは誤字を直したり、考えを変えたりできなければなりません。待たせることが製品の要件に合っているかを、まず判断する必要があります。この実験では、新しい検索を許可し、もう有効でないレスポンスを捨てるポリシーを選びます。

どう動くのか

入力中のdraftは、まだ提出されていない文です。提出されたリクエストには、検索語と提出番号を持たせます。同じ検索語を再び提出しても、新しい意図になりうるからです。画面の状態は、次のように区別します。

状態 画面が把握していること ユーザーに必要な案内
idle まだ提出された検索がない 検索語の入力
loading 特定のリクエストのレスポンス待ち 何を待っているのか
success リクエストに対応する結果の一覧 どの検索の結果なのか
empty レスポンスは成功したが、結果がない 検索語の変更
error リクエストの失敗とその原因 再試行の方法

loading: boolean、error: boolean、items: Result[]を別々に管理すると、ローディングとエラーが同時にオンになる組み合わせも作れてしまいます。kindを基準に分けたユニオン型は、各状態が持つべき情報をひとまとめにします。successでだけitemsを読み、errorでだけmessageを読むようにすれば、レンダリング関数の中でも矛盾を見つけやすくなります。ただし、型チェックはレスポンスの順序を保証しません。猫とクジラは、どちらも正しい型の結果だからです。

出来事表を先に書いてみましょう。番号はミリ秒ではなく、順序です。

出来事 内容 表示する検索
1 猫のリクエスト 猫を待機中
2 クジラのリクエスト クジラを待機中
3 クジラのレスポンス クジラの結果
4 猫のレスポンス 依然としてクジラの結果

最後の行が、実装すべき製品ポリシーです。レスポンスが成功したという事実と、今も有効だという事実を、分けて説明できなければなりません。

データではなく、取りうる状態を設計する

次の2つの状態を比較してみましょう。例の文字列は形式を説明するための見本で、実際のレスポンス値はリクエストによって変わります。

type View =
  | { kind: 'idle' }
  | { kind: 'loading'; query: string }
  | { kind: 'success'; query: string; titles: string[] }
  | { kind: 'error'; query: string; message: string };

const waiting: View = { kind: 'loading', query: '별 지도' };
const failed: View = {
  kind: 'error', query: '별 지도', message: '자료실에 연결할 수 없습니다'
};

2つの状態を同時にオンにする代わりに、現在の状態を1つ選びます。errorを選んだなら、どのリクエストが失敗したのかと、案内の文が一緒にある必要があります。実際のラボでは、ここにemptyを加えます。新しい状態を追加するときは、型定義を変えるだけで終わらせず、画面の文言と遷移の検査も一緒に探す必要があります。

失敗のあとに再試行してloadingに移ったのに、古いエラーの文がまだ見えているなら、状態と画面のつながりがずれているのです。新しいリクエストを作る関数だけを見ず、レンダリングの分岐がどのフィールドを読んでいるかを確認しましょう。逆に、結果が先に消えるのが常に正解とは限りません。前の結果を残したまま新しい結果を待つ製品もあります。そのポリシーを選ぶなら、「前の結果」という表示と、現在のリクエスト状態を表現する型が、追加で必要です。この実験は、前の結果を空にする単純なポリシーで、競合に集中します。

現場での姿

検索だけでなく、住所のオートコンプリート、商品オプションごとの在庫、地図の位置変更、顧客ごとの詳細パネルでも、同じ問題が起きます。FDEが、顧客へのデモ中に画面がときどき過去の値に切り替わる現象に出会ったら、まず入力時刻とレスポンス時刻を一緒に記録する必要があります。失敗のログだけを探すと、成功した古いレスポンスを見逃します。

2026-09-11に確認したCanonical Web Developerの求人は、TypeScript・レスポンシブUI・アクセシビリティ・複雑なUIのパフォーマンスを求めており、大規模なReact+TypeScriptの経験は、歓迎要件として区別しています。この課題は、その要求を、小さく観察できる作業に置き換えたものです。EMEAの求人1件が、市場全体の採用頻度や合格を保証するものではありません。

自分で確認すること

実験室のリクエストキューで、クジラを先に、猫をあとに配信します。入力欄と結果のタイトルをそれぞれ読み、どの時点で2つがずれたのかを記録します。開始コードの型チェックが通っているのに、なぜ画面が間違いうるのかを、1文で説明してみましょう。

型の表現は、TypeScriptのdiscriminated unionsの説明を参照してください。