ネットワーク並行性管理
Webアプリケーションを構築する際、ネットワークリクエストの管理は困難な作業となることがあります。最新のデータを保証し、同時リクエストを処理する課題は、アプリケーション内で中断や競合状態に対処するための複雑なロジックにつながることがよくあります。Remixは、Webブラウザの直感的な動作を反映および拡張することで、ネットワーク管理を自動化し、このプロセスを簡素化します。
Remixの仕組みを理解するために、フルスタックデータフローで説明したように、formの送信後、Remixはローダーから新しいデータをフェッチすることを思い出してください。これは再検証と呼ばれます。
ブラウザの動作との自然な整合性
Remixのネットワーク並行性の処理は、ドキュメントを処理する際のWebブラウザのデフォルトの動作に大きく影響を受けています。
-
ブラウザのリンクナビゲーション: ブラウザでリンクをクリックし、ページ遷移が完了する前に別のリンクをクリックすると、ブラウザは最新の
actionを優先します。最初の要求をキャンセルし、最後にクリックしたリンクのみに焦点を当てます。- Remixのアプローチ: Remixは、クライアント側のナビゲーションを同じように管理します。Remixアプリケーション内でリンクがクリックされると、ターゲットURLに関連付けられた各ローダーに対してフェッチリクエストが開始されます。最初のナビゲーションが別のナビゲーションによって中断された場合、Remixは以前のフェッチリクエストをキャンセルし、最新のリクエストのみが続行されるようにします。
-
ブラウザのフォーム送信: ブラウザでフォーム送信を開始し、すぐに別のフォームを再度送信すると、ブラウザは最初の送信を無視し、最新の送信のみを処理します。
- Remixのアプローチ: Remixは、フォームを操作する際にこの動作を模倣します。フォームが送信され、最初の送信が完了する前に別の送信が発生した場合、Remixは元のフェッチリクエストをキャンセルします。次に、最新の送信が完了するのを待ってから、ページ再検証を再度トリガーします。
同時送信と再検証
標準的なブラウザは、ナビゲーションとフォーム送信に対して一度に1つのリクエストに制限されていますが、Remixはこの動作を向上させます。ナビゲーションとは異なり、useFetcherを使用すると、複数のリクエストを同時に実行できます。
Remixは、サーバーのactionへの複数のフォーム送信と、同時再検証リクエストを効率的に処理するように設計されています。新しいデータが利用可能になるとすぐに、状態が迅速に更新されるようにします。ただし、Remixは、他のactionが競合状態を引き起こす場合に、古いデータをコミットしないことで、潜在的な落とし穴から保護します。
たとえば、3つのフォーム送信が進行中で、1つが完了した場合、Remixは他の2つを待たずに、そのデータでUIをすぐに更新し、UIが応答性と動的な状態を維持するようにします。残りの送信が完了すると、RemixはUIの更新を続け、最新のデータが表示されるようにします。
いくつかの視覚化を理解するために、以下に図で使用される記号のキーを示します。
|: 送信開始- ✓: アクション完了、データ再検証開始
- ✅: 再検証されたデータがUIにコミットされる
- ❌: リクエストキャンセル
submission 1: |----✓-----✅
submission 2: |-----✓-----✅
submission 3: |-----✓-----✅ただし、後続の送信の再検証が以前の送信よりも先に完了した場合、Remixは以前のデータを破棄し、UIに最新の情報のみが反映されるようにします。
submission 1: |----✓---------❌
submission 2: |-----✓-----✅
submission 3: |-----✓-----✅送信 (2) からの再検証は後で開始され、送信 (1) よりも早く完了したため、送信 (1) からのリクエストはキャンセルされ、送信 (2) からのデータのみがUIにコミットされます。後でリクエストされたため、(1) と (2) の両方からの更新された値が含まれている可能性が高くなります。
古いデータの可能性
ユーザーがこれを経験することはまずありませんが、インフラストラクチャに一貫性がない非常にまれな状況で、ユーザーが古いデータを見る可能性はまだあります。Remixは古いデータの要求をキャンセルしますが、それらはサーバーに到達することになります。ブラウザでリクエストをキャンセルすると、そのリクエストのブラウザリソースが解放されるだけで、「追いついて」サーバーへの到達を阻止することはできません。非常にまれな状況では、キャンセルされたリクエストが、中断したactionの再検証が完了した後にデータを変更する可能性があります。次の図を考えてみましょう。
👇 interruption with new submission
|----❌----------------------✓
|-------✓-----✅
👆
initial request reaches the server
after the interrupting submission
has completed revalidationユーザーは現在、サーバー上のデータとは異なるデータを見ています。この問題は非常にまれであり、デフォルトのブラウザの動作にも存在することに注意してください。最初の要求が、2番目の送信と再検証の両方よりも遅れてサーバーに到達する可能性は、どのネットワークおよびサーバーインフラストラクチャでも予期されていません。これがインフラストラクチャで懸念される場合は、フォーム送信にタイムスタンプを送信し、古い送信を無視するサーバーロジックを作成できます。
例
コンボボックスのようなUIコンポーネントでは、各キーストロークがネットワークリクエストをトリガーする可能性があります。このような高速で連続したリクエストを管理することは、特に表示される結果が最新のクエリと一致するようにする場合、難しい場合があります。ただし、Remixを使用すると、この課題は自動的に処理され、開発者がネットワークをマイクロ管理することなく、ユーザーが正しい結果を確認できるようになります。
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
export async function loader({
request,
}: LoaderFunctionArgs) {
const { searchParams } = new URL(request.url);
const cities = await searchCities(searchParams.get("q"));
return json(cities);
}
export function CitySearchCombobox() {
const fetcher = useFetcher<typeof loader>();
return (
<fetcher.Form action="/city-search">
<Combobox aria-label="Cities">
<ComboboxInput
name="q"
onChange={(event) =>
// onChange でフォームを送信して都市のリストを取得します
fetcher.submit(event.target.form)
}
/>
{/* ローダーのデータでレンダリング */}
{fetcher.data ? (
<ComboboxPopover className="shadow-popup">
{fetcher.data.length > 0 ? (
<ComboboxList>
{fetcher.data.map((city) => (
<ComboboxOption
key={city.id}
value={city.name}
/>
))}
</ComboboxList>
) : (
<span>結果が見つかりません</span>
)}
</ComboboxPopover>
) : null}
</Combobox>
</fetcher.Form>
);
}アプリケーションが知る必要があるのは、データのクエリ方法とレンダリング方法だけで、Remixがネットワークを処理します。
結論
Remixは、開発者にネットワークリクエストを管理するための直感的でブラウザベースのアプローチを提供します。ブラウザの動作を反映し、必要に応じて拡張することで、並行性、再検証、および潜在的な競合状態の複雑さを簡素化します。シンプルなWebページを構築する場合でも、高度なWebアプリケーションを構築する場合でも、Remixはユーザーインタラクションがスムーズで信頼性が高く、常に最新の状態であることを保証します。