ネットワーク並行性管理

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 にコミットされる
  • ❌: リクエストキャンセル
送信 1: |----✓-----✅
送信 2:    |-----✓-----✅
送信 3:             |-----✓-----✅

ただし、後続の送信の再検証が以前の送信よりも先に完了した場合、Remix は以前のデータを破棄し、UI に最新の情報のみが反映されるようにします。

送信 1: |----✓---------❌
送信 2:    |-----✓-----✅
送信 3:             |-----✓-----✅

送信 (2) からの再検証は後で開始され、送信 (1) よりも早く完了したため、送信 (1) からのリクエストはキャンセルされ、送信 (2) からのデータのみが UI にコミットされます。後でリクエストされたため、(1) と (2) の両方からの更新された値が含まれている可能性が高くなります。

古いデータの可能性

ユーザーがこれを経験することはまずありませんが、インフラストラクチャに一貫性がない非常にまれな状況で、ユーザーが古いデータを見る可能性はまだあります。Remix は古いデータの要求をキャンセルしますが、それらはサーバーに到達することになります。ブラウザでリクエストをキャンセルすると、そのリクエストのブラウザリソースが解放されるだけで、「追いついて」サーバーへの到達を阻止することはできません。非常にまれな状況では、キャンセルされたリクエストが、中断した action の再検証が完了した後にデータを変更する可能性があります。次の図を考えてみましょう。

     👇 新しい送信による中断
|----❌----------------------✓
       |-------✓-----✅
                             👆
                  中断した送信の後、
                  最初の要求がサーバーに到達
                  再検証が完了しました

ユーザーは現在、サーバー上のデータとは異なるデータを見ています。この問題は非常にまれであり、デフォルトのブラウザの動作にも存在することに注意してください。最初の要求が、2 番目の送信と再検証の両方よりも遅れてサーバーに到達する可能性は、どのネットワークおよびサーバーインフラストラクチャでも予期されていません。これがインフラストラクチャで懸念される場合は、フォーム送信にタイムスタンプを送信し、古い送信を無視するサーバーロジックを作成できます。

コンボボックスのような UI コンポーネントでは、各キーストロークがネットワークリクエストをトリガーする可能性があります。このような高速で連続したリクエストを管理することは、特に表示される結果が最新のクエリと一致するようにする場合、難しい場合があります。ただし、Remix を使用すると、この課題は自動的に処理され、開発者がネットワークをマイクロ管理することなく、ユーザーが正しい結果を確認できるようになります。

app/routes/city-search.tsx
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 はユーザーインタラクションがスムーズで信頼性が高く、常に最新の状態であることを保証します。