Skip to content

Instantly share code, notes, and snippets.

@mfrancois3k
Last active June 3, 2022 01:08
Show Gist options
  • Save mfrancois3k/ceb8ab4cc79cdf525b342ea18a3ec52e to your computer and use it in GitHub Desktop.
Save mfrancois3k/ceb8ab4cc79cdf525b342ea18a3ec52e to your computer and use it in GitHub Desktop.
//Refences
//https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src%2FApp.tsx
https://gist.github.com/mfrancois3k/ceb8ab4cc79cdf525b342ea18a3ec52e
import * as React from "react";
import {
Routes,
Route,
Link,
useNavigate,
useLocation,
Navigate,
Outlet
} from "react-router-dom";
import { fakeAuthProvider } from "./auth";
export default function App() {
return (
<AuthProvider>
<h1>Auth Example</h1>
<p>
This example demonstrates a simple login flow with three pages: a public
page, a protected page, and a login page. In order to see the protected
page, you must first login. Pretty standard stuff.
</p>
<p>
First, visit the public page. Then, visit the protected page. You're not
yet logged in, so you are redirected to the login page. After you login,
you are redirected back to the protected page.
</p>
<p>
Notice the URL change each time. If you click the back button at this
point, would you expect to go back to the login page? No! You're already
logged in. Try it out, and you'll see you go back to the page you
visited just *before* logging in, the public page.
</p>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<PublicPage />} />
<Route path="/login" element={<LoginPage />} />
<Route
path="/protected"
element={
<RequireAuth>
<ProtectedPage />
</RequireAuth>
}
/>
</Route>
</Routes>
</AuthProvider>
);
}
function Layout() {
return (
<div>
<AuthStatus />
<ul>
<li>
<Link to="/">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Outlet />
</div>
);
}
interface AuthContextType {
user: any;
signin: (user: string, callback: VoidFunction) => void;
signout: (callback: VoidFunction) => void;
}
let AuthContext = React.createContext<AuthContextType>(null!);
function AuthProvider({ children }: { children: React.ReactNode }) {
let [user, setUser] = React.useState<any>(null);
let signin = (newUser: string, callback: VoidFunction) => {
return fakeAuthProvider.signin(() => {
setUser(newUser);
callback();
});
};
let signout = (callback: VoidFunction) => {
return fakeAuthProvider.signout(() => {
setUser(null);
callback();
});
};
let value = { user, signin, signout };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
function useAuth() {
return React.useContext(AuthContext);
}
function AuthStatus() {
let auth = useAuth();
let navigate = useNavigate();
if (!auth.user) {
return <p>You are not logged in.</p>;
}
return (
<p>
Welcome {auth.user}!{" "}
<button
onClick={() => {
auth.signout(() => navigate("/"));
}}
>
Sign out
</button>
</p>
);
}
function RequireAuth({ children }: { children: JSX.Element }) {
let auth = useAuth();
let location = useLocation();
if (!auth.user) {
// Redirect them to the /login page, but save the current location they were
// trying to go to when they were redirected. This allows us to send them
// along to that page after they login, which is a nicer user experience
// than dropping them off on the home page.
return <Navigate to="/login" state={{ from: location }} />;
}
return children;
}
function LoginPage() {
let navigate = useNavigate();
let location = useLocation();
let auth = useAuth();
let from = location.state?.from?.pathname || "/";
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
let formData = new FormData(event.currentTarget);
let username = formData.get("username") as string;
auth.signin(username, () => {
// Send them back to the page they tried to visit when they were
// redirected to the login page. Use { replace: true } so we don't create
// another entry in the history stack for the login page. This means that
// when they get to the protected page and click the back button, they
// won't end up back on the login page, which is also really nice for the
// user experience.
navigate(from, { replace: true });
});
}
return (
<div>
<p>You must log in to view the page at {from}</p>
<form onSubmit={handleSubmit}>
<label>
Username: <input name="username" type="text" />
</label>{" "}
<button type="submit">Login</button>
</form>
</div>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/multi-app?file=inbox%2FApp.jsx
import { Routes, Route, useParams, Link, Outlet } from "react-router-dom";
import "./index.css";
import { getMessageById, messages } from "./messages";
import { NoMatch } from "./no-match";
export default function InboxApp() {
return (
<Routes>
{/* Routes in this app don't need to worry about which URL prefix they are
mounted at. They can just assume they are mounted at /. Then, if they
are moved under a different basename later on, all routes and links will
continue to work. */}
<Route path="/" element={<Layout />}>
<Route index element={<Inbox />} />
<Route path=":id" element={<Message />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
);
}
function Layout() {
return (
<div>
<h1>Welcome to the Inbox app!</h1>
<nav>
<ul>
<li>
{/* Using a normal link here will cause the browser to reload the
document when following this link, which is exactly what we want
when transitioning to the "Home" app so we execute its entry
point (see home/main.jsx). */}
<a href="/">Home</a>
</li>
<li>
<a href="/about">About</a>
</li>
<li>
<Link to="/">Inbox</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
}
function Inbox() {
return (
<div>
<div style={{ maxWidth: 800, margin: "0 auto" }}>
{messages.map((message) => (
<Link
to={message.id}
key={message.id}
style={{
display: "flex",
borderBottom: "1px solid #ccc",
padding: "10px",
width: "100%",
textDecoration: "none",
color: "#000",
}}
>
<span
style={{
flexBasis: 100,
marginRight: "1rem",
}}
>
{message.from.name}
</span>
<div
style={{
flexGrow: 1,
textOverflow: "ellipsis",
width: "100%",
whiteSpace: "nowrap",
overflow: "hidden",
marginRight: "1rem",
}}
>
<span>{message.subject}</span>
<div style={{ color: "#999", display: "inline" }}>
<span>{" — "}</span>
<span>{message.body}</span>
</div>
</div>
<span style={{ flexShrink: 0 }}>
{new Date(message.date).toDateString()}
</span>
</Link>
))}
</div>
</div>
);
}
function Message() {
let { id } = useParams();
let message = getMessageById(id);
if (!message) {
return <NoMatch />;
}
return (
<div>
<h2>{message.subject}</h2>
<div>
<h3 style={{ fontSize: 14 }}>
<span>{message.from.name}</span>{" "}
<span>&lt;{message.from.email}&gt;</span>
</h3>
<div>{message.body}</div>
</div>
</div>
);
}
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/ssr?file=src%2FApp.tsx
import * as React from "react";
import { Routes, Route, Outlet, Link } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Server Rendering Example</h1>
<p>
If you check out the HTML source of this page, you'll notice that it
already contains the HTML markup of the app that was sent from the
server!
</p>
<p>
This is great for search engines that need to index this page. It's also
great for users because server-rendered pages tend to load more quickly
on mobile devices and over slow networks.
</p>
<p>
Another thing to notice is that when you click one of the links below
and navigate to a different URL, then hit the refresh button on your
browser, the server is able to generate the HTML markup for that page as
well because you're using React Router on the server. This creates a
seamless experience both for your users navigating around your site and
for developers on your team who get to use the same routing library in
both places.
</p>
{/* Routes nest inside one another. Nested route paths build upon
parent route paths, and nested route elements render inside
parent route elements. See the note about <Outlet> below. */}
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />} />
{/* Using path="*"" means "match anything", so this route
acts like a catch-all for URLs that we don't have explicit
routes for. */}
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</div>
);
}
function Layout() {
return (
<div>
{/* A "layout route" is a good place to put markup you want to
share across all the pages on your site, like navigation. */}
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/nothing-here">Nothing Here</Link>
</li>
</ul>
</nav>
<hr />
{/* An <Outlet> renders whatever child route is currently active,
so you can think about this <Outlet> as a placeholder for
the child routes we defined above. */}
<Outlet />
</div>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
//Refences
//https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src%2FApp.tsx
import * as React from "react";
import {
Routes,
Route,
Link,
useNavigate,
useLocation,
Navigate,
Outlet
} from "react-router-dom";
import { fakeAuthProvider } from "./auth";
export default function App() {
return (
<AuthProvider>
<h1>Auth Example</h1>
<p>
This example demonstrates a simple login flow with three pages: a public
page, a protected page, and a login page. In order to see the protected
page, you must first login. Pretty standard stuff.
</p>
<p>
First, visit the public page. Then, visit the protected page. You're not
yet logged in, so you are redirected to the login page. After you login,
you are redirected back to the protected page.
</p>
<p>
Notice the URL change each time. If you click the back button at this
point, would you expect to go back to the login page? No! You're already
logged in. Try it out, and you'll see you go back to the page you
visited just *before* logging in, the public page.
</p>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<PublicPage />} />
<Route path="/login" element={<LoginPage />} />
<Route
path="/protected"
element={
<RequireAuth>
<ProtectedPage />
</RequireAuth>
}
/>
</Route>
</Routes>
</AuthProvider>
);
}
function Layout() {
return (
<div>
<AuthStatus />
<ul>
<li>
<Link to="/">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Outlet />
</div>
);
}
interface AuthContextType {
user: any;
signin: (user: string, callback: VoidFunction) => void;
signout: (callback: VoidFunction) => void;
}
let AuthContext = React.createContext<AuthContextType>(null!);
function AuthProvider({ children }: { children: React.ReactNode }) {
let [user, setUser] = React.useState<any>(null);
let signin = (newUser: string, callback: VoidFunction) => {
return fakeAuthProvider.signin(() => {
setUser(newUser);
callback();
});
};
let signout = (callback: VoidFunction) => {
return fakeAuthProvider.signout(() => {
setUser(null);
callback();
});
};
let value = { user, signin, signout };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
function useAuth() {
return React.useContext(AuthContext);
}
function AuthStatus() {
let auth = useAuth();
let navigate = useNavigate();
if (!auth.user) {
return <p>You are not logged in.</p>;
}
return (
<p>
Welcome {auth.user}!{" "}
<button
onClick={() => {
auth.signout(() => navigate("/"));
}}
>
Sign out
</button>
</p>
);
}
function RequireAuth({ children }: { children: JSX.Element }) {
let auth = useAuth();
let location = useLocation();
if (!auth.user) {
// Redirect them to the /login page, but save the current location they were
// trying to go to when they were redirected. This allows us to send them
// along to that page after they login, which is a nicer user experience
// than dropping them off on the home page.
return <Navigate to="/login" state={{ from: location }} />;
}
return children;
}
function LoginPage() {
let navigate = useNavigate();
let location = useLocation();
let auth = useAuth();
let from = location.state?.from?.pathname || "/";
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
let formData = new FormData(event.currentTarget);
let username = formData.get("username") as string;
auth.signin(username, () => {
// Send them back to the page they tried to visit when they were
// redirected to the login page. Use { replace: true } so we don't create
// another entry in the history stack for the login page. This means that
// when they get to the protected page and click the back button, they
// won't end up back on the login page, which is also really nice for the
// user experience.
navigate(from, { replace: true });
});
}
return (
<div>
<p>You must log in to view the page at {from}</p>
<form onSubmit={handleSubmit}>
<label>
Username: <input name="username" type="text" />
</label>{" "}
<button type="submit">Login</button>
</form>
</div>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
// reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/basic?file=src%2FApp.tsx
import * as React from "react";
import { Routes, Route, Outlet, Link } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Basic Example</h1>
<p>
This example demonstrates some of the core features of React Router
including nested <code>&lt;Route&gt;</code>s,{" "}
<code>&lt;Outlet&gt;</code>s, <code>&lt;Link&gt;</code>s, and using a
"*" route (aka "splat route") to render a "not found" page when someone
visits an unrecognized URL.
</p>
{/* Routes nest inside one another. Nested route paths build upon
parent route paths, and nested route elements render inside
parent route elements. See the note about <Outlet> below. */}
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />} />
{/* Using path="*"" means "match anything", so this route
acts like a catch-all for URLs that we don't have explicit
routes for. */}
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</div>
);
}
function Layout() {
return (
<div>
{/* A "layout route" is a good place to put markup you want to
share across all the pages on your site, like navigation. */}
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/nothing-here">Nothing Here</Link>
</li>
</ul>
</nav>
<hr />
{/* An <Outlet> renders whatever child route is currently active,
so you can think about this <Outlet> as a placeholder for
the child routes we defined above. */}
<Outlet />
</div>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/custom-query-parsing?file=src%2FApp.tsx
import * as React from "react";
import * as JSURL from "jsurl";
import type { NavigateOptions } from "react-router-dom";
import { Routes, Route, Link, useSearchParams } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Custom Query Parsing Example</h1>
<p>
This example demonstrates how to store a complex data structure in a URL
query parameter.
</p>
<p>
Each time a field in the form below changes, the URL is updated with a
serialized version of the form's values. To see the effect this has,
manipulate some fields in the form. Then, copy the URL in the address
bar and paste it into a new tab in your browser to see the form in the
exact same state as when you left it!
</p>
<Routes>
<Route index element={<Home />} />
<Route path="*" element={<NoMatch />} />
</Routes>
</div>
);
}
/**
* This custom hook is a wrapper around `useSearchParams()` that parses and
* serializes the search param value using the JSURL library, which permits any
* JavaScript value to be safely URL-encoded.
*
* It's a good example of how React hooks offer a great deal of flexibility when
* you compose them together!
*
* TODO: rethink the generic type here, users can put whatever they want in the
* URL, probably best to use runtime validation with a type predicate:
* https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
*/
function useQueryParam<T>(
key: string
): [T | undefined, (newQuery: T, options?: NavigateOptions) => void] {
let [searchParams, setSearchParams] = useSearchParams();
let paramValue = searchParams.get(key);
let value = React.useMemo(() => JSURL.parse(paramValue), [paramValue]);
let setValue = React.useCallback(
(newValue: T, options?: NavigateOptions) => {
let newSearchParams = new URLSearchParams(searchParams);
newSearchParams.set(key, JSURL.stringify(newValue));
setSearchParams(newSearchParams, options);
},
[key, searchParams, setSearchParams]
);
return [value, setValue];
}
interface Pizza {
toppings: string[];
crust: string;
extraSauce: boolean;
}
function Home() {
let [pizza, setPizza] = useQueryParam<Pizza>("pizza");
if (!pizza) {
pizza = { toppings: [], crust: "regular", extraSauce: false };
}
function handleChange(event: React.ChangeEvent<HTMLFormElement>) {
let form = event.currentTarget;
let formData = new FormData(form);
// This complex data structure is preserved in the URL in the
// `pizza` query parameter each time a value in the form changes!
let pizza: Pizza = {
toppings: formData.getAll("toppings") as string[],
crust: formData.get("crust") as string,
extraSauce: formData.get("extraSauce") === "on"
};
setPizza(pizza, { replace: true });
}
return (
<div>
<form onChange={handleChange}>
<p>What would you like on your pizza?</p>
<p>
<label>
<input
defaultChecked={pizza.toppings.includes("pepperoni")}
type="checkbox"
name="toppings"
value="pepperoni"
/>{" "}
Pepperoni
</label>
<br />
<label>
<input
defaultChecked={pizza.toppings.includes("bell-peppers")}
type="checkbox"
name="toppings"
value="bell-peppers"
/>{" "}
Bell Peppers
</label>
<br />
<label>
<input
type="checkbox"
name="toppings"
value="olives"
defaultChecked={pizza.toppings.includes("olives")}
/>{" "}
Olives
</label>
</p>
<p>
<label>
<input
type="radio"
name="crust"
value="regular"
defaultChecked={pizza.crust === "regular"}
/>{" "}
Regular Crust
</label>
<br />
<label>
<input
type="radio"
name="crust"
value="thin"
defaultChecked={pizza.crust === "thin"}
/>{" "}
Thin Crust
</label>
<br />
<label>
<input
type="radio"
name="crust"
value="deep-dish"
defaultChecked={pizza.crust === "dish-dish"}
/>{" "}
Deep Dish
</label>
</p>
<p>
<label>
<input
type="checkbox"
name="extraSauce"
defaultChecked={pizza.extraSauce}
/>{" "}
Extra Sauce
</label>
</p>
</form>
<hr />
<p>The current form values are:</p>
<pre>{JSON.stringify(pizza || {}, null, 2)}</pre>
</div>
);
}
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/lazy-loading?file=src%2FApp.tsx
import * as React from "react";
import { Routes, Route, Outlet, Link } from "react-router-dom";
const About = React.lazy(() => import("./pages/About"));
const Dashboard = React.lazy(() => import("./pages/Dashboard"));
export default function App() {
return (
<div>
<h1>Lazy Loading Example</h1>
<p>
This example demonstrates how to lazily load both route elements and
even entire portions of your route hierarchy on demand. To get the full
effect of this demo, be sure to open your Network tab and watch the new
bundles load dynamically as you navigate around.
</p>
<p>
The "About" page is not loaded until you click on the link. When you do,
a <code>&lt;React.Suspense fallback&gt;</code> renders while the code is
loaded via a dynamic <code>import()</code> statement. Once the code
loads, the fallback is replaced by the actual code for that page.
</p>
<p>
The "Dashboard" page does the same thing, but takes it even one step
further by <em>dynamically defining additional routes</em> once the page
loads! Since React Router lets you declare your routes as
<code>&lt;Route&gt;</code> elements, you can easily define more routes
by placing an additional <code>&lt;Routes&gt;</code> element anywhere
further down the element tree. Just be sure the parent route ends with a{" "}
<code>*</code> like <code>&lt;Route path="dashboard/*"&gt;</code> in
this case.
</p>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route
path="about"
element={
<React.Suspense fallback={<>...</>}>
<About />
</React.Suspense>
}
/>
<Route
path="dashboard/*"
element={
<React.Suspense fallback={<>...</>}>
<Dashboard />
</React.Suspense>
}
/>
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</div>
);
}
function Layout() {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard/messages">Messages (Dashboard)</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/modal?file=src%2FApp.tsx
import * as React from "react";
import {
Routes,
Route,
Outlet,
Link,
useLocation,
useNavigate,
useParams
} from "react-router-dom";
import { Dialog } from "@reach/dialog";
import "@reach/dialog/styles.css";
import { IMAGES, getImageById } from "./images";
export default function App() {
let location = useLocation();
// The `backgroundLocation` state is the location that we were at when one of
// the gallery links was clicked. If it's there, use it as the location for
// the <Routes> so we show the gallery in the background, behind the modal.
let state = location.state as { backgroundLocation?: Location };
return (
<div>
<h1>Modal Example</h1>
<p>
This is an example of how to create a contextual modal navigation with
React Router where the navigation path the user takes determines if the
page is rendered in the modal or not (popularized by pinterest,
instagram, and others in the 2010s). This type of modal is typically
used as a kind of "detail" view to focus on a particular object in a
collection (like a pinterest board) while not taking you completely out
of context of the parent page. But, when the same URL is visited
directly (rather than from the collection page) it renders as it's own
full page instead of in a modal.
</p>
<p>
In this example, notice how the URL updates when the modal opens (if you
are viewing the example in StackBlitz you may need to open in a new
browser window). Even though the URL is updated to the specific item in
the modal, the background page is still showing behind it.
</p>
<p>
Next, copy and paste the URL to a new browser tab and notice that it
shows that specific item not in a modal, but directly on the page. This
is the view that someone would see if they clicked on a link that you
sent them when you had the modal open. They don't have the context you
did when you opened the modal, so they don't see it in the context of
the background page.
</p>
<Routes location={state?.backgroundLocation || location}>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="gallery" element={<Gallery />} />
<Route path="/img/:id" element={<ImageView />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
{/* Show the modal when a `backgroundLocation` is set */}
{state?.backgroundLocation && (
<Routes>
<Route path="/img/:id" element={<Modal />} />
</Routes>
)}
</div>
);
}
function Layout() {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/gallery">Gallery</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
<h3>Featured Images</h3>
<ul>
<li>
<Link to="/img/1">Image 1</Link>
</li>
<li>
<Link to="/img/2">Image 2</Link>
</li>
</ul>
</div>
);
}
function Gallery() {
let location = useLocation();
return (
<div style={{ padding: "0 24px" }}>
<h2>Gallery</h2>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
gap: "24px"
}}
>
{IMAGES.map(image => (
<Link
key={image.id}
to={`/img/${image.id}`}
// This is the trick! Set the `backgroundLocation` in location state
// so that when we open the modal we still see the current page in
// the background.
state={{ backgroundLocation: location }}
>
<img
width={200}
height={200}
style={{
width: "100%",
aspectRatio: "1 / 1",
height: "auto",
borderRadius: "8px"
}}
src={image.src}
alt={image.title}
/>
</Link>
))}
</div>
</div>
);
}
function ImageView() {
let { id } = useParams<"id">();
let image = getImageById(Number(id));
if (!image) return <div>Image not found</div>;
return (
<div>
<h1>{image.title}</h1>
<img width={400} height={400} src={image.src} alt="" />
</div>
);
}
function Modal() {
let navigate = useNavigate();
let { id } = useParams<"id">();
let image = getImageById(Number(id));
let buttonRef = React.useRef<HTMLButtonElement>(null);
function onDismiss() {
navigate(-1);
}
if (!image) return null;
return (
<Dialog
aria-labelledby="label"
onDismiss={onDismiss}
initialFocusRef={buttonRef}
>
<div
style={{
display: "grid",
justifyContent: "center",
padding: "8px 8px"
}}
>
<h1 id="label" style={{ margin: 0 }}>
{image.title}
</h1>
<img
style={{
margin: "16px 0",
borderRadius: "8px",
width: "100%",
height: "auto"
}}
width={400}
height={400}
src={image.src}
alt=""
/>
<button
style={{ display: "block" }}
ref={buttonRef}
onClick={onDismiss}
>
Close
</button>
</div>
</Dialog>
);
}
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
// reference
https://stackblitz.com/edit/react-router-active-classname-ejgsgl?file=src%2FApp.js
// also use this repo as link for example for react app
https://github.com/Adrion10/react-nested-routes-main/tree/master/src
import React from 'react';
import {
BrowserRouter as Router,
Routes,
Route,
NavLink,
} from 'react-router-dom';
import './style.css';
const Users = () => <div>Users</div>;
const Posts = () => <div>Posts</div>;
const Tests = () => <div>Tests</div>;
const App = () => {
return (
<div className="app">
<Router>
<div className="nav">
<NavLink
to="users"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Users
</NavLink>
<NavLink
to="posts"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Posts
</NavLink>
<NavLink
to="tests"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Tests
</NavLink>
</div>
<Routes>
<Route path="users" element={<Users />} />
<Route path="posts" element={<Posts />} />
<Route path="tests" element={<Tests />} />
</Routes>
</Router>
</div>
);
};
export default App;
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/route-objects?file=src%2FApp.tsx
import * as React from "react";
import type { RouteObject } from "react-router-dom";
import { Outlet, Link, useRoutes, useParams } from "react-router-dom";
export default function App() {
let routes: RouteObject[] = [
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{
path: "/courses",
element: <Courses />,
children: [
{ index: true, element: <CoursesIndex /> },
{ path: "/courses/:id", element: <Course /> },
],
},
{ path: "*", element: <NoMatch /> },
],
},
];
// The useRoutes() hook allows you to define your routes as JavaScript objects
// instead of <Routes> and <Route> elements. This is really just a style
// preference for those who prefer to not use JSX for their routes config.
let element = useRoutes(routes);
return (
<div>
<h1>Route Objects Example</h1>
<p>
This example demonstrates how to use React Router's "route object" API
instead of the JSX API to configure your routes. Both APIs are
first-class. In fact, React Router actually uses the object-based API
internally by creating route objects from your{" "}
<code>&lt;Route&gt;</code>
elements.
</p>
<p>
React Router exposes a <code>useRoutes()</code> hook that allows you to
hook into the same matching algorithm that <code>&lt;Routes&gt;</code>{" "}
uses internally to decide which <code>&lt;Route&gt;</code> to render.
When you use this hook, you get back an element that will render your
entire route hierarchy.
</p>
{element}
</div>
);
}
function Layout() {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/courses">Courses</Link>
</li>
<li>
<Link to="/nothing-here">Nothing Here</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function Courses() {
return (
<div>
<h2>Courses</h2>
<Outlet />
</div>
);
}
function CoursesIndex() {
return (
<div>
<p>Please choose a course:</p>
<nav>
<ul>
<li>
<Link to="react-fundamentals">React Fundamentals</Link>
</li>
<li>
<Link to="advanced-react">Advanced React</Link>
</li>
<li>
<Link to="react-router">React Router</Link>
</li>
</ul>
</nav>
</div>
);
}
function Course() {
let { id } = useParams<"id">();
return (
<div>
<h2>
Welcome to the {id!.split("-").map(capitalizeString).join(" ")} course!
</h2>
<p>This is a great course. You're gonna love it!</p>
<Link to="/courses">See all courses</Link>
</div>
);
}
function capitalizeString(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1);
}
function NoMatch() {
return (
<div>
<h2>It looks like you're lost...</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
https://stackblitz.com/edit/navigate-to-url-query-strings-search-params-react-ibdj48?file=src%2FApp.js
import React, { useEffect } from 'react';
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
NavLink,
useNavigate,
createSearchParams,
} from 'react-router-dom';
import './style.css';
const useNavigateSearch = () => {
const navigate = useNavigate();
return (pathname, params) =>
navigate(`${pathname}?${createSearchParams(params)}`);
};
const Users = () => {
const navigateSearch = useNavigateSearch();
const goToPosts = () =>
navigateSearch('/posts', { sort: 'gg', order: 'newest' });
return (
<div>
<p>Users</p>
<button onClick={goToPosts}>Go to Posts</button>
</div>
);
};
const Posts = () => {
const navigateSearch = useNavigateSearch();
const goToUser = () =>
navigateSearch('/users', { test: 'ooo', how: 'it works' });
return (
<div>
<>User</>
<button onClick={goToUser}> Go To User </button>
</div>
);
};
const App = () => {
return (
<div className="app">
<Router>
<div className="nav">
<NavLink
to="users"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Users
</NavLink>
<NavLink
to="posts"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Posts
</NavLink>
</div>
<Routes>
<Route path="users" element={<Users />} />
<Route path="users" element={<Users />} />
<Route path="posts" element={<Posts />} />
<Route path="posts" element={<Posts />} />
<Route path="" element={<Navigate to="/users" />} />
<Route path="" element={<Navigate to="/posts" />} />
</Routes>
</Router>
</div>
);
};
export default App;
//Reference
https://stackblitz.com/github/remix-run/react-router/tree/main/examples/search-params?file=src%2FApp.tsx
import * as React from "react";
import { Link, Route, Routes, useSearchParams } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Search Params Example</h1>
<p>
This example demonstrates a simple search page that makes a request for
user data to the GitHub API and displays information for that user on
the page. The example uses the <code>useSearchParams()</code> hook to
read and write the URL query string.
</p>
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<NoMatch />} />
</Routes>
</div>
);
}
function randomUser() {
let users = ["chaance", "jacob-ebey", "mcansh", "mjackson", "ryanflorence"];
return users[Math.floor(Math.random() * users.length)];
}
function Home() {
let [searchParams, setSearchParams] = useSearchParams();
// searchParams is a URLSearchParams object.
// See https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
let user = searchParams.get("user");
let [userData, setUserData] = React.useState<any>(null);
React.useEffect(() => {
let abortController = new AbortController();
async function getGitHubUser() {
let response = await fetch(`https://api.github.com/users/${user}`, {
signal: abortController.signal,
});
if (!abortController.signal.aborted) {
let data = await response.json();
setUserData(data);
}
}
if (user) {
getGitHubUser();
}
return () => {
abortController.abort();
};
}, [user]);
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
let formData = new FormData(event.currentTarget);
let newUser = formData.get("user") as string;
if (!newUser) return;
setSearchParams({ user: newUser });
}
function handleRandomSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
let newUser = randomUser();
// our new random user is the same as our current one, let's try again
if (newUser === user) {
handleRandomSubmit(event);
} else {
setSearchParams({ user: newUser });
}
}
return (
<div>
<div style={{ display: "flex" }}>
<form onSubmit={handleSubmit}>
<label>
<input defaultValue={user ?? undefined} type="text" name="user" />
</label>
<button type="submit">Search</button>
</form>
<form onSubmit={handleRandomSubmit}>
<input type="hidden" name="random" />
<button type="submit">Random</button>
</form>
</div>
{userData && (
<div
style={{
padding: "24px",
margin: "24px 0",
borderTop: "1px solid #eaeaea",
display: "flex",
alignItems: "center",
gap: "16px",
}}
>
<img
style={{ borderRadius: "50%" }}
width={200}
height={200}
src={userData.avatar_url}
alt={userData.login}
/>
<div>
<h2>{userData.name}</h2>
<p>{userData.bio}</p>
</div>
</div>
)}
</div>
);
}
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment