13. Pending UI и Transitions
Remix предоставляет хук useNavigation для отслеживания состояния навигации и отправки форм. Это позволяет показывать индикаторы загрузки и блокировать кнопки во время выполнения запросов.
useNavigation
Заголовок раздела «useNavigation»import { useNavigation } from "@remix-run/react";
export default function Page() { const navigation = useNavigation();
// Состояния: "idle" | "loading" | "submitting" const isLoading = navigation.state === "loading"; const isSubmitting = navigation.state === "submitting";
return ( <div> {isLoading && <div className="progress-bar" />}
<Form method="post"> <button type="submit" disabled={isSubmitting}> {isSubmitting ? "Сохранение..." : "Сохранить"} </button> </Form> </div> );}Глобальный индикатор загрузки
Заголовок раздела «Глобальный индикатор загрузки»// В root.tsx или layout компонентеimport { useNavigation } from "@remix-run/react";
function GlobalLoadingIndicator() { const navigation = useNavigation(); const isLoading = navigation.state !== "idle";
return ( <div style={{ position: "fixed", top: 0, left: 0, height: "3px", width: isLoading ? "70%" : "0%", background: "#3992ff", transition: isLoading ? "width 0.5s ease" : "width 0.1s ease", }} /> );}Определение активной формы
Заголовок раздела «Определение активной формы»const navigation = useNavigation();
// Определяем какая именно форма отправляетсяconst isCreating = navigation.formAction === "/todos" && navigation.state === "submitting";
const isDeleting = navigation.formData?.get("intent") === "delete" && navigation.state === "submitting";
// Конкретный itemconst deletingId = navigation.state === "submitting" ? navigation.formData?.get("todoId") : null;useFetcher состояния
Заголовок раздела «useFetcher состояния»function SaveButton({ itemId }) { const fetcher = useFetcher(); const isSaving = fetcher.state !== "idle";
return ( <fetcher.Form method="post" action="/save"> <input type="hidden" name="id" value={itemId} /> <button type="submit" disabled={isSaving}> {isSaving ? ( <span> <Spinner /> Сохранение... </span> ) : ( "Сохранить" )} </button> </fetcher.Form> );}Pending навигация
Заголовок раздела «Pending навигация»import { Link, useNavigation } from "@remix-run/react";
function NavItem({ to, children }) { const navigation = useNavigation();
// Определяем, загружается ли этот маршрут const isPending = navigation.state === "loading" && navigation.location?.pathname === to;
return ( <Link to={to} style={{ opacity: isPending ? 0.6 : 1, fontWeight: isPending ? "bold" : "normal", }} > {isPending ? "⏳ " : ""}{children} </Link> );}