-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Closed
Labels
Description
I'm using React Router as a...
framework
Reproduction
System Info
[email protected]
Ok to proceed? (y) y
System:
OS: macOS 15.3.1
CPU: (10) arm64 Apple M1 Pro
Memory: 438.13 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.18.3 - ~/.volta/tools/image/node/20.18.3/bin/node
npm: 10.8.2 - ~/.volta/tools/image/node/20.18.3/bin/npm
pnpm: 10.5.2 - ~/.volta/bin/pnpm
Browsers:
Chrome: 134.0.6998.89
Safari: 18.3
npmPackages:
@react-router/dev: ^7.3.0 => 7.3.0
@react-router/node: ^7.3.0 => 7.3.0
@react-router/serve: ^7.3.0 => 7.3.0
react-router: ^7.3.0 => 7.3.0
vite: ^6.2.2 => 6.2.2Used Package Manager
pnpm
Expected Behavior
How to implement loading when lazy loading
Actual Behavior
How to implement loading when lazy loading
- layout
import { Suspense } from "react";
import { Outlet, useLocation, useNavigate } from "react-router";
import { Button } from "~/components/ui/button";
export default function Layout() {
const location = useLocation();
const navigate = useNavigate();
return (
<div>
<h1>Header</h1>
<Button onClick={() => navigate("/about")}>About</Button>
<Button onClick={() => navigate("/user")}>User</Button>
<Suspense fallback="loading..." key={location.key}>
<Outlet />
</Suspense>
</div>
);
}- routes.ts
import { type RouteConfig, layout, route } from "@react-router/dev/routes";
export default [layout("./routes/layout.tsx",
[route("about", "./routes/about.tsx"),
route("user", "./routes/user.tsx")])] satisfies RouteConfig;- react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
// Config options...
// Server-side render by default, to enable SPA mode set this to `false`
ssr: false,
} satisfies Config;- root.tsx
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
import type { Route } from "./+types/root";
import "./app.css";
export const links: Route.LinksFunction = () => [
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
{
rel: "preconnect",
href: "https://fonts.gstatic.com",
crossOrigin: "anonymous"
},
{
rel: "stylesheet",
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
}
];
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
export default function App() {
return <Outlet />;
}
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = "Oops!";
let details = "An unexpected error occurred.";
let stack: string | undefined;
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? "404" : "Error";
details = error.status === 404 ? "The requested page could not be found." : error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
}
return (
<main className="pt-16 p-4 container mx-auto">
<h1>{message}</h1>
<p>{details}</p>
{stack && (
<pre className="w-full p-4 overflow-x-auto">
<code>{stack}</code>
</pre>
)}
</main>
);
}
export function HydrateFallback() {
return (
<main className="pt-16 p-4 container mx-auto">
<h1>整个页面的Loading...</h1>
</main>
);
}