竞态条件

竞态条件

虽然不可能完全消除应用程序中的所有竞态条件,但 React Router 会自动处理 Web 用户界面中最常见的竞态条件。

浏览器行为

React Router 处理网络并发的方式深受 Web 浏览器处理文档时行为的启发。

设想你点击了一个链接,指向一个新文档,然后在新页面加载完成之前又点击了另一个不同的链接。浏览器会

  1. 取消第一个请求
  2. 立即处理新的导航

同样的行为也适用于表单提交。当一个挂起的表单提交被一个新的提交中断时,第一个提交会被取消,新的提交会立即被处理。

React Router 行为

与浏览器类似,通过链接和表单提交中断的导航会取消正在进行的(in flight)数据请求,并立即处理新的事件。

Fetcher 有点更微妙,因为它们不像导航那样是单例事件。Fetcher 不能中断其他 fetcher 实例,但它们可以中断自己,并且行为与其他情况相同:取消被中断的请求,立即处理新的请求。

然而,Fetcher 在重新验证时会相互作用。当 fetcher 的 action 请求返回到浏览器后,会发送一个针对所有页面数据的重新验证。这意味着多个重新验证请求可以同时进行中(in-flight)。React Router 会提交所有“新鲜”的重新验证响应,并取消任何过时的请求。过时的请求是指任何开始时间早于已返回的请求。

这种网络管理可以防止由网络竞态条件引起的常见 UI 错误。

由于网络不可预测,并且你的服务器仍在处理这些已取消的请求,你的后端仍可能遇到竞态条件并存在潜在的数据完整性问题。这些风险与使用纯 HTML <forms> 的默认浏览器行为时的风险相同,我们认为这些风险较低,并且超出 React Router 的范围。

实际益处

考虑构建一个预输入(type-ahead)组合框。当用户输入时,你向服务器发送请求。当他们每输入一个新字符时,你发送一个新的请求。重要的是不要向用户显示不再存在于文本字段中的值的结果。

使用 fetcher 时,这会自动为你处理。考虑以下伪代码:

// 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 会自动取消该 fetcher 上挂起的请求。这确保你不会向用户显示与当前输入值不同的请求结果。

文档和示例 CC 4.0