レースコンディション

競合状態

アプリケーション内のあらゆる競合状態を排除することは不可能ですが、React RouterはWebユーザーインターフェースで最も一般的な競合状態を自動的に処理します。

ブラウザの動作

React Routerのネットワーク同時実行処理は、ブラウザがドキュメントを処理する際の動作に大きく影響を受けています。

新しいドキュメントへのリンクをクリックし、新しいページが読み込み終わる前に別のリンクをクリックすることを考えてみてください。ブラウザは

  1. 最初のリクエストをキャンセルし、
  2. すぐに新しいナビゲーションを処理します。

同じ動作はフォーム送信にも適用されます。保留中のフォーム送信が新しいフォーム送信によって中断されると、最初の送信はキャンセルされ、新しい送信がすぐに処理されます。

React Routerの動作

ブラウザと同様に、リンクやフォーム送信による中断されたナビゲーションは、処理中のデータリクエストをキャンセルし、すぐに新しいイベントを処理します。

フェッチャーは、ナビゲーションのようなシングルトンイベントではないため、少し微妙です。フェッチャーは他のフェッチャーインスタンスを中断することはできませんが、自身を中断することはでき、その動作は他のすべてと同じです。中断されたリクエストをキャンセルし、すぐに新しいリクエストを処理します。

ただし、フェッチャーは再検証に関しては相互に作用します。フェッチャーのアクションリクエストがブラウザに戻ると、すべてのページデータの再検証が送信されます。これは、複数の再検証リクエストが同時に処理中になる可能性があることを意味します。React Routerはすべての「最新」の再検証レスポンスをコミットし、古いリクエストをキャンセルします。古いリクエストとは、返されたリクエストよりも*早く*開始されたリクエストのことです。

このネットワーク管理により、ネットワークの競合状態によって引き起こされる最も一般的なUIバグを防ぎます。

ネットワークは予測不可能であり、サーバーはこれらのキャンセルされたリクエストを処理し続けるため、バックエンドでは競合状態が発生し、データの整合性に問題が生じる可能性があります。これらのリスクは、プレーンHTMLの<forms>でデフォルトのブラウザの動作を使用する場合と同じリスクであり、React Routerの範囲外であると見なされます。

実用的な利点

先行入力コンボボックスの作成を考えてみましょう。ユーザーが入力するたびに、サーバーにリクエストを送信します。新しい文字を入力するたびに、新しいリクエストを送信します。テキストフィールドにない値の結果をユーザーに表示しないことが重要です。

フェッチャーを使用すると、これは自動的に管理されます。この疑似コードを考えてみましょう。

// route("/city-search", "./search-cities.ts")
export async function loader({ request }) {
  const { searchParams } = new URL(request.url);
  return searchCities(searchParams.get("q"));
}
export function CitySearchCombobox() {
  const fetcher = useFetcher();

  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)
          }
        />

        {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>
  );
}

fetcher.submitの呼び出しは、そのフェッチャーで保留中のリクエストを自動的にキャンセルします。これにより、異なる入力値のリクエストの結果がユーザーに表示されることがなくなります。

ドキュメントと例 CC 4.0