ダッシュボードに6枚のカード、6つのスピナー、それらは一緒に表示されません — ドミノ倒しのように一つずつ表示されます。各カードは上のカードが完了してからロードを開始したからです。これがウォーターフォールで、合計待ち時間は最も遅いリクエストではなく、すべての合計です。use()とSuspenseはその連鎖を断ち切ります — しかし面白いのは内部のメカニズムです:サスペンドされたコンポーネントが実際に何をスローするのか、そして間違った順序で届いたHTMLがどのように正しい場所に収まるのか。
マイクロインタラクションの記事では、transformとopacityがコンポジタースレッド上でアニメーションするので、重いページでもボタンのアニメーションがカクつくことはないはずだと書いた。それは今も真実だ——ただし、あの記事が触れていなかった一つの留保がある。メインスレッドが長いJSタスクによってブロックされていれば、アニメーションはそもそも起動する機会すら得られないかもしれない。クリックのハンドラ自体がキューで待たされているからだ。INPはまさにこのギャップを測る指標であり、scheduler.yield()はそれを実際に埋めるための数少ない手段の一つである。
千件のコメントを含むリストのレンダリングが遅いので react-window に手を伸ばす——すると、なめらかなスクロールと引き換えに、Ctrl+F、ページの印刷、そしてスクリーンリーダーのナビゲーションの一部を失うことになる。表示ウィンドウの外にある要素が、DOMから物理的に存在しなくなってしまうからだ。content-visibility はまったく別の角度から同じパフォーマンス問題を解決する。要素はDOMに残ったままで、ブラウザはただ、要素が表示領域の近くに来るまで、レイアウトや描画といったコストの高い処理をその要素に対して省略するだけだ。そして驚くべきことに、テキストを検索したときには一時的にその要素を『見える状態』に戻すことさえできる。
アクセシビリティの記事で取り上げた、`display: none`で隠されているのにアクセシビリティツリーには残り続けていたモーダルは、divをゼロから組み立ててモーダルを作ることに起因するミスの一例でした。本当に問うべきなのは、そもそもなぜゼロから作らなければならなかったのか、ということです。ネイティブな`<dialog>`とpopover属性が無料で提供してくれるもの——top layer、フォーカストラップ、light dismiss——を確認し、この二つの仕組みがどこでどれほど違うのか、その違いを取り違えることがいかに簡単にアクセシビリティを欠いたインターフェースへの近道になるかを見ていきます。
「いいね」ボタンは即座に反応し、ハートが塗りつぶされ、カウンターが増える——サーバーがまだ何一つ応答していないうちに。これはマイクロインタラクションの記事で扱った速さの錯覚よりも一歩踏み込んだものです。インターフェースがまだ存在しない状態を表示し、それがすぐに実現すると仮定しているのです。この仮定が外れたときに本当に嘘を撤回できる、安全な作り方を確認します。そして、手動の`setState`と`catch`が、`useOptimistic`が提供するものの劣化版でしかない理由も見ていきます。
「use server」は、APIルートへのfetch呼び出しに対する単なるシンタックスシュガーのように見えます。だからこそ、多くの実装はこれをただの関数呼び出しとして扱ってしまいます。これは具体的な代償を伴う誤解です。「use server」と記されたすべての関数は、ネットワーク上の公開エンドポイントになります。そして、この仕組みの上に作られたフォームは、JavaScriptが読み込まれる前から動作します。このディレクティブの裏側で実際に何が起きているのか、そしてドキュメントを一目見ただけでは分からない落とし穴がどこにあるのかを確認していきます。