Skip to content

Commit

Permalink
Update logic and add Marks examples
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Feb 3, 2023
1 parent 566672b commit 36975ff
Show file tree
Hide file tree
Showing 15 changed files with 471 additions and 106 deletions.
7 changes: 7 additions & 0 deletions .changeset/many-frogs-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"react-router": minor
"react-router-dom": minor
"@remix-run/router": minor
---

Add support for `route.lazy` for code-splitting and lazy-loading of route modules
63 changes: 63 additions & 0 deletions examples/data-router/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,40 @@ let router = createBrowserRouter(
loader={deferredLoader}
element={<DeferredPage />}
/>
<Route
path="lazy"
lazy={async () => {
console.log("loading lazy");
await sleep(1000);
console.log("done loading lazy");
let {
default: Component,
loader,
action,
ErrorBoundary,
shouldRevalidate,
} = await import("./lazy");

return {
element: <Component />,
loader,
action,
shouldRevalidate,
...(ErrorBoundary
? {
errorElement: <ErrorBoundary />,
hasErrorBoundary: true,
}
: {}),
};
}}
/>
</Route>
)
);

window.router = router;

if (import.meta.hot) {
import.meta.hot.dispose(() => router.dispose());
}
Expand All @@ -68,6 +98,7 @@ export function Fallback() {
export function Layout() {
let navigation = useNavigation();
let revalidator = useRevalidator();
let lazyFetcher = useFetcher();
let fetchers = useFetchers();
let fetcherInProgress = fetchers.some((f) =>
["loading", "submitting"].includes(f.state)
Expand All @@ -94,14 +125,46 @@ export function Layout() {
<li>
<Link to="/deferred">Deferred</Link>
</li>
<li>
<Link to="/lazy">Lazy</Link>
</li>{" "}
<li>
<Link to="/404">404 Link</Link>
</li>
<li>
<Form method="post" action="/lazy">
<button type="submit">Post to /lazy</button>
</Form>
</li>
<li>
<button onClick={() => revalidator.revalidate()}>
Revalidate Data
</button>
</li>
<li>
<button onClick={() => lazyFetcher.load("/lazy")}>
Load /lazy via fetcher
</button>
&nbsp;&nbsp;
<span>
fetcher state/data: {lazyFetcher.state}/
{JSON.stringify(lazyFetcher.data)}
</span>
</li>
<li>
<button
onClick={() =>
lazyFetcher.submit({}, { method: "post", action: "/lazy" })
}
>
Submit to /lazy via fetcher
</button>
&nbsp;&nbsp;
<span>
fetcher state/data: {lazyFetcher.state}/
{JSON.stringify(lazyFetcher.data)}
</span>
</li>
</ul>
</nav>
<div style={{ position: "fixed", top: 0, right: 0 }}>
Expand Down
70 changes: 70 additions & 0 deletions examples/data-router/src/lazy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from "react";
import type {
ActionFunction,
ShouldRevalidateFunction,
} from "react-router-dom";
import { Form, useLoaderData } from "react-router-dom";

interface LazyLoaderData {
date: string;
submissionCount: number;
}

let submissionCount = 0;

export const loader = async (): Promise<LazyLoaderData> => {
console.log("lazy loader start");
await new Promise((r) => setTimeout(r, 1000));
console.log("lazy loader end");
return {
date: new Date().toISOString(),
submissionCount,
};
};

export const action: ActionFunction = async ({ request }) => {
console.log("lazy action start");
await new Promise((r) => setTimeout(r, 1000));
console.log("lazy action end");

let body = await request.formData();
if (body.get("error")) {
throw new Error("Form action error");
}

submissionCount++;
return submissionCount;
};

export function ErrorBoundary() {
return (
<>
<h2>Lazy error boundary</h2>
<pre>Something went wrong</pre>
</>
);
}

export const shouldRevalidate: ShouldRevalidateFunction = (args) => {
return Boolean(args.formAction);
};

export default function LazyPage() {
let data = useLoaderData() as LazyLoaderData;

return (
<>
<h2>Lazy</h2>
<p>Date from loader: {data.date}</p>
<p>Form submission count: {data.submissionCount}</p>
<Form method="post">
<div style={{ display: "flex", gap: 12 }}>
<button>Submit form</button>
<button name="error" value="true">
Throw an error
</button>
</div>
</Form>
</>
);
}
50 changes: 26 additions & 24 deletions examples/ssr-data-router/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions examples/ssr-data-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
},
"dependencies": {
"@remix-run/node": "^1.7.0",
"@remix-run/router": "^1.0.0",
"@remix-run/router": "^1.3.1",
"compression": "1.7.4",
"cross-env": "^7.0.3",
"express": "^4.17.1",
"history": "^5.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.0"
"react-router-dom": "^6.8.0"
},
"devDependencies": {
"@rollup/plugin-replace": "^3.0.0",
Expand Down
33 changes: 32 additions & 1 deletion examples/ssr-data-router/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ export const routes = [
loader: dashboardLoader,
element: <Dashboard />,
},
{
path: "lazy",
async lazy() {
console.log("start lazy()");
await sleep(1000);
console.log("end lazy()");
let {
default: Component,
loader,
action,
ErrorBoundary,
shouldRevalidate,
} = await import("./lazy");

return {
element: <Component />,
loader,
action,
shouldRevalidate,
...(ErrorBoundary
? {
errorElement: <ErrorBoundary />,
hasErrorBoundary: true,
}
: {}),
};
},
},
{
path: "redirect",
loader: redirectLoader,
Expand Down Expand Up @@ -70,6 +98,9 @@ function Layout() {
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/lazy">Lazy</Link>
</li>
<li>
<Link to="/redirect">Redirect to Home</Link>
</li>
Expand All @@ -86,7 +117,7 @@ function Layout() {
);
}

const sleep = () => new Promise((r) => setTimeout(r, 500));
const sleep = (n = 500) => new Promise((r) => setTimeout(r, n));
const rand = () => Math.round(Math.random() * 100);

async function homeLoader() {
Expand Down
34 changes: 28 additions & 6 deletions examples/ssr-data-router/src/entry.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,31 @@ import { routes } from "./App";

let router = createBrowserRouter(routes);

ReactDOM.hydrateRoot(
document.getElementById("app"),
<React.StrictMode>
<RouterProvider router={router} fallbackElement={null} />
</React.StrictMode>
);
// If you're using lazy route modules and you haven't yet preloaded them onto
// routes, then you'll need to wait for the router to be initialized before
// hydrating, since it will have initial data to hydrate but it won't yet have
// any router elements to render.
//
// This shouldn't be needed in most SSR stacks as you should know what routes
// are initially rendered and be able to SSR the appropriate <Script> tags for
// those modules such that they're readily available on your client-side route
// definitions
if (!router.state.initialized) {
let unsub = router.subscribe((state) => {
if (state.initialized) {
unsub();
hydrate();
}
});
} else {
hydrate();
}

function hydrate() {
ReactDOM.hydrateRoot(
document.getElementById("app"),
<React.StrictMode>
<RouterProvider router={router} fallbackElement={null} />
</React.StrictMode>
);
}
Loading

0 comments on commit 36975ff

Please sign in to comment.