ユーザーが新しいルートに移動したり、アクションにデータを送信したりする場合、UIはユーザーの操作に対して、保留中または楽観的な状態を即座に返す必要があります。これはアプリケーションコードの責任です。
ユーザーが新しいURLに移動すると、次のページのローダーが完了するまで待機し、その後、次のページがレンダリングされます。保留中の状態は、useNavigation
から取得できます。
import { useNavigation } from "react-router";
export default function Root() {
const navigation = useNavigation();
const isNavigating = Boolean(navigation.location);
return (
<html>
<body>
{isNavigating && <GlobalSpinner />}
<Outlet />
</body>
</html>
);
}
保留中のインジケーターは、リンクにローカライズすることもできます。NavLinkの子要素、className、およびstyleのpropsは、保留中の状態を受け取る関数にすることができます。
import { NavLink } from "react-router";
function Navbar() {
return (
<nav>
<NavLink to="/home">
{({ isPending }) => (
<span>Home {isPending && <Spinner />}</span>
)}
</NavLink>
<NavLink
to="/about"
style={({ isPending }) => ({
color: isPending ? "gray" : "black",
})}
>
About
</NavLink>
</nav>
);
}
フォームが送信されると、UIは保留中の状態でユーザーの操作に即座に応答する必要があります。これは、独自の独立した状態を持つ(通常のフォームはグローバルなナビゲーションを引き起こす)fetcherフォームを使用するのが最も簡単です。
import { useFetcher } from "react-router";
function NewProjectForm() {
const fetcher = useFetcher();
return (
<fetcher.Form method="post">
<input type="text" name="title" />
<button type="submit">
{fetcher.state !== "idle"
? "Submitting..."
: "Submit"}
</button>
</fetcher.Form>
);
}
fetcherではないフォーム送信の場合、保留中の状態はuseNavigation
で利用できます。
import { useNavigation, Form } from "react-router";
function NewProjectForm() {
const navigation = useNavigation();
return (
<Form method="post" action="/projects/new">
<input type="text" name="title" />
<button type="submit">
{navigation.formAction === "/projects/new"
? "Submitting..."
: "Submit"}
</button>
</Form>
);
}
フォーム送信データによってUIの将来の状態がわかっている場合、瞬時のUXのために楽観的なUIを実装できます。
function Task({ task }) {
const fetcher = useFetcher();
let isComplete = task.status === "complete";
if (fetcher.formData) {
isComplete = fetcher.formData.get("status");
}
return (
<div>
<div>{task.title}</div>
<fetcher.Form method="post">
<button
name="status"
value={isComplete ? "incomplete" : "complete"}
>
{isComplete ? "Mark Incomplete" : "Mark Complete"}
</button>
</fetcher.Form>
</div>
);
}
次へ: テスト