ネットワーク並行処理の管理

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) =>
            // submit the form onChange to get the list of cities
            fetcher.submit(event.target.form)
          }
        />
 
        {/* render with the loader's data */}
        {fetcher.data ? (
          <ComboboxPopover className="shadow-popup">
            {fetcher.data.length > 0 ? (
              <ComboboxList>
                {fetcher.data.map((city) => (
                  <ComboboxOption
                    key={city.id}
                    value={city.name}
                  />
                ))}
              </ComboboxList>
            ) : (
              <span>No results found</span>
            )}
          </ComboboxPopover>
        ) : null}
      </Combobox>
    </fetcher.Form>
  );
}

アプリケーションが知る必要があるのは、データをクエリする方法とレンダリングする方法だけです。Remix はネットワークを処理します。

まとめ

Remix は、開発者にネットワークリクエストを管理するための直感的でブラウザベースのアプローチを提供します。ブラウザの動作を反映し、必要に応じて強化することで、並行処理、再検証、潜在的な競合状態の複雑さを簡素化します。シンプルな Web ページでも高度な Web アプリケーションでも、Remix は、ユーザーの操作がスムーズで、信頼性が高く、常に最新の状態であることを保証します。