虽然无法消除应用程序中每一种可能的竞态条件,但 React Router 会自动处理 Web 用户界面中最常见的竞态条件。
React Router 处理网络并发的方式深受 Web 浏览器处理文档行为的启发。
考虑点击一个链接到一个新文档,然后在新页面完成加载之前点击另一个不同的链接。浏览器将
相同的行为适用于表单提交。当一个待处理的表单提交被新的提交中断时,第一个会被取消,新的提交会被立即处理。
像浏览器一样,通过链接和表单提交中断的导航将取消正在进行的数据请求,并立即处理新事件。
Fetchers 有些细微差别,因为它们不像导航那样是单例事件。Fetchers 不能中断其他 fetcher 实例,但它们可以中断自身,并且行为与其他所有情况相同:取消被中断的请求并立即处理新请求。
然而,在重新验证方面,Fetchers 确实会相互交互。在 fetcher 的 action 请求返回到浏览器后,会发送所有页面数据的重新验证。这意味着可能会同时存在多个正在进行的重新验证请求。React Router 将提交所有“新鲜”的重新验证响应,并取消任何过时的请求。过时的请求是指任何开始时间早于已返回的请求。
这种网络管理方式可以防止由网络竞态条件引起的最常见的 UI 错误。
由于网络是不可预测的,并且您的服务器仍然会处理这些取消的请求,因此您的后端仍然可能遇到竞态条件并存在潜在的数据完整性问题。这些风险与使用带有纯 HTML <forms>
的默认浏览器行为的风险相同,我们认为这些风险很低,并且超出 React Router 的范围。
考虑构建一个自动完成组合框。当用户输入时,您向服务器发送请求。当他们输入每个新字符时,您发送一个新的请求。重要的是不要向用户显示不再在文本字段中的值的结果。
当使用 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 上的待处理请求。这确保您永远不会向用户显示针对不同输入值的请求的结果。