From 5b1765f54ee1f769b23c4ded3ad02f04a34e636e Mon Sep 17 00:00:00 2001
From: Matt Brophy
Date: Wed, 12 Jul 2023 17:26:25 -0400
Subject: [PATCH 01/12] Add auth example using RouterProvider (#10698)
---
examples/auth-router-provider/.gitignore | 5 +
examples/auth-router-provider/.stackblitzrc | 4 +
examples/auth-router-provider/README.md | 28 +
examples/auth-router-provider/index.html | 12 +
.../auth-router-provider/package-lock.json | 1475 +++++++++++++++++
examples/auth-router-provider/package.json | 23 +
examples/auth-router-provider/src/App.tsx | 205 +++
examples/auth-router-provider/src/auth.ts | 24 +
examples/auth-router-provider/src/index.css | 12 +
examples/auth-router-provider/src/main.tsx | 11 +
.../auth-router-provider/src/vite-env.d.ts | 1 +
examples/auth-router-provider/tsconfig.json | 21 +
examples/auth-router-provider/vite.config.ts | 39 +
13 files changed, 1860 insertions(+)
create mode 100644 examples/auth-router-provider/.gitignore
create mode 100644 examples/auth-router-provider/.stackblitzrc
create mode 100644 examples/auth-router-provider/README.md
create mode 100644 examples/auth-router-provider/index.html
create mode 100644 examples/auth-router-provider/package-lock.json
create mode 100644 examples/auth-router-provider/package.json
create mode 100644 examples/auth-router-provider/src/App.tsx
create mode 100644 examples/auth-router-provider/src/auth.ts
create mode 100644 examples/auth-router-provider/src/index.css
create mode 100644 examples/auth-router-provider/src/main.tsx
create mode 100644 examples/auth-router-provider/src/vite-env.d.ts
create mode 100644 examples/auth-router-provider/tsconfig.json
create mode 100644 examples/auth-router-provider/vite.config.ts
diff --git a/examples/auth-router-provider/.gitignore b/examples/auth-router-provider/.gitignore
new file mode 100644
index 0000000000..d451ff16c1
--- /dev/null
+++ b/examples/auth-router-provider/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
diff --git a/examples/auth-router-provider/.stackblitzrc b/examples/auth-router-provider/.stackblitzrc
new file mode 100644
index 0000000000..d98146f4d0
--- /dev/null
+++ b/examples/auth-router-provider/.stackblitzrc
@@ -0,0 +1,4 @@
+{
+ "installDependencies": true,
+ "startCommand": "npm run dev"
+}
diff --git a/examples/auth-router-provider/README.md b/examples/auth-router-provider/README.md
new file mode 100644
index 0000000000..f18f905f1e
--- /dev/null
+++ b/examples/auth-router-provider/README.md
@@ -0,0 +1,28 @@
+---
+title: Authentication (using RouterProvider)
+toc: false
+---
+
+# Auth Example (using RouterProvider)
+
+This example demonstrates how to restrict access to routes to authenticated users when using ``.
+
+The primary difference compared to how authentication was handled in `BrowserRouter` is that since `RouterProvider` decouples fetching from rendering, we can no longer rely on React context and/or hooks to get our user authentication status. We need access to this information outside of the React tree so we can use it in our route `loader` and `action` functions.
+
+For some background information on this design choice, please check out the [Remixing React Router](https://remix.run/blog/remixing-react-router) blog post and Ryan's [When to Fetch](https://www.youtube.com/watch?v=95B8mnhzoCM) talk from Reactathon.
+
+Be sure to pay attention to the following features in this example:
+
+- The use of a standalone object _outside of the React tree_ that manages our authentication state
+- The use of `loader` functions to check for user authentication
+- The use of `redirect` from the `/protexted` `loader` when the user is not logged in
+- The use of a `
} />
+ );
+}
+
+function Layout() {
+ return (
+
+
Auth Example using RouterProvider
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+
+
+
+ Public Page
+
+
+ Protected Page
+
+
+
+
+
+ );
+}
+
+function AuthStatus() {
+ // Get our logged in user, if they exist, from the root route loader data
+ let { user } = useRouteLoaderData("root") as { user: string | null };
+ let fetcher = useFetcher();
+
+ if (!user) {
+ return
+ );
+}
+
+async function loginAction({ request }: LoaderFunctionArgs) {
+ let formData = await request.formData();
+ let username = formData.get("username") as string | null;
+
+ // Validate our form inputs and return validation errors via useActionData()
+ if (!username) {
+ return {
+ error: "You must provide a username to log in",
+ };
+ }
+
+ // Sign in and redirect to the proper destination if successful.
+ try {
+ await fakeAuthProvider.signin(username);
+ } catch (error) {
+ // Unused as of now but this is how you would handle invalid
+ // username/password combinations - just like validating the inputs
+ // above
+ return {
+ error: "Invalid login attempt",
+ };
+ }
+
+ let redirectTo = formData.get("redirectTo") as string | null;
+ return redirect(redirectTo || "/");
+}
+
+async function loginLoader() {
+ if (fakeAuthProvider.isAuthenticated) {
+ return redirect("/");
+ }
+ return null;
+}
+
+function LoginPage() {
+ let location = useLocation();
+ let params = new URLSearchParams(location.search);
+ let from = params.get("from") || "/";
+
+ let navigation = useNavigation();
+ let isLoggingIn = navigation.formData?.get("username") != null;
+
+ let actionData = useActionData() as { error: string } | undefined;
+
+ return (
+
+
You must log in to view the page at {from}
+
+
+
+ );
+}
+
+function PublicPage() {
+ return
Public
;
+}
+
+function protectedLoader({ request }: LoaderFunctionArgs) {
+ // If the user is not logged in and tries to access `/protected`, we redirect
+ // them to `/login` with a `from` parameter that allows login to redirect back
+ // to this page upon successful authentication
+ if (!fakeAuthProvider.isAuthenticated) {
+ let params = new URLSearchParams();
+ params.set("from", new URL(request.url).pathname);
+ return redirect("/login?" + params.toString());
+ }
+ return null;
+}
+
+function ProtectedPage() {
+ return
Protected
;
+}
diff --git a/examples/auth-router-provider/src/auth.ts b/examples/auth-router-provider/src/auth.ts
new file mode 100644
index 0000000000..52d5dce2d4
--- /dev/null
+++ b/examples/auth-router-provider/src/auth.ts
@@ -0,0 +1,24 @@
+interface AuthProvider {
+ isAuthenticated: boolean;
+ username: null | string;
+ signin(username: string): Promise;
+ signout(): Promise;
+}
+
+/**
+ * This represents some generic auth provider API, like Firebase.
+ */
+export const fakeAuthProvider: AuthProvider = {
+ isAuthenticated: false,
+ username: null,
+ async signin(username: string) {
+ await new Promise((r) => setTimeout(r, 500)); // fake delay
+ fakeAuthProvider.isAuthenticated = true;
+ fakeAuthProvider.username = username;
+ },
+ async signout() {
+ await new Promise((r) => setTimeout(r, 500)); // fake delay
+ fakeAuthProvider.isAuthenticated = false;
+ fakeAuthProvider.username = "";
+ },
+};
diff --git a/examples/auth-router-provider/src/index.css b/examples/auth-router-provider/src/index.css
new file mode 100644
index 0000000000..3e1f253f03
--- /dev/null
+++ b/examples/auth-router-provider/src/index.css
@@ -0,0 +1,12 @@
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
+ "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
+ monospace;
+}
diff --git a/examples/auth-router-provider/src/main.tsx b/examples/auth-router-provider/src/main.tsx
new file mode 100644
index 0000000000..4b8bb578ac
--- /dev/null
+++ b/examples/auth-router-provider/src/main.tsx
@@ -0,0 +1,11 @@
+import * as React from "react";
+import * as ReactDOM from "react-dom/client";
+
+import "./index.css";
+import App from "./App";
+
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+
+);
diff --git a/examples/auth-router-provider/src/vite-env.d.ts b/examples/auth-router-provider/src/vite-env.d.ts
new file mode 100644
index 0000000000..11f02fe2a0
--- /dev/null
+++ b/examples/auth-router-provider/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/auth-router-provider/tsconfig.json b/examples/auth-router-provider/tsconfig.json
new file mode 100644
index 0000000000..429c4c3629
--- /dev/null
+++ b/examples/auth-router-provider/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "target": "ESNext",
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "importsNotUsedAsValues": "error"
+ },
+ "include": ["./src"]
+}
diff --git a/examples/auth-router-provider/vite.config.ts b/examples/auth-router-provider/vite.config.ts
new file mode 100644
index 0000000000..fbadfa5d9f
--- /dev/null
+++ b/examples/auth-router-provider/vite.config.ts
@@ -0,0 +1,39 @@
+import * as path from "path";
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import rollupReplace from "@rollup/plugin-replace";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ server: {
+ port: 3000,
+ },
+ plugins: [
+ rollupReplace({
+ preventAssignment: true,
+ values: {
+ __DEV__: JSON.stringify(true),
+ "process.env.NODE_ENV": JSON.stringify("development"),
+ },
+ }),
+ react(),
+ ],
+ resolve: process.env.USE_SOURCE
+ ? {
+ alias: {
+ "@remix-run/router": path.resolve(
+ __dirname,
+ "../../packages/router/index.ts"
+ ),
+ "react-router": path.resolve(
+ __dirname,
+ "../../packages/react-router/index.ts"
+ ),
+ "react-router-dom": path.resolve(
+ __dirname,
+ "../../packages/react-router-dom/index.tsx"
+ ),
+ },
+ }
+ : {},
+});
From 4e12473040de76abf26e1374c23a19d29d78efc0 Mon Sep 17 00:00:00 2001
From: kylegirard <1444753+kylegirard@users.noreply.github.com>
Date: Sat, 15 Jul 2023 11:49:26 -0400
Subject: [PATCH 02/12] Fix syntax highlighting in docs for useOutletContext
(#10708)
---
contributors.yml | 1 +
docs/hooks/use-outlet-context.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/contributors.yml b/contributors.yml
index 5a596e1e99..f7b3e5542e 100644
--- a/contributors.yml
+++ b/contributors.yml
@@ -119,6 +119,7 @@
- koojaa
- KostiantynPopovych
- KutnerUri
+- kylegirard
- landisdesign
- latin-1
- lequangdongg
diff --git a/docs/hooks/use-outlet-context.md b/docs/hooks/use-outlet-context.md
index 5b5794e008..73642afed3 100644
--- a/docs/hooks/use-outlet-context.md
+++ b/docs/hooks/use-outlet-context.md
@@ -36,7 +36,7 @@ function Child() {
If you're using TypeScript, we recommend the parent component provide a custom hook for accessing the context value. This makes it easier for consumers to get nice typings, control consumers, and know who's consuming the context value. Here's a more realistic example:
-```tsx filename=src/routes/dashboard.tsx lines=[13, 19]
+```tsx filename=src/routes/dashboard.tsx lines=[13,19]
import * as React from "react";
import type { User } from "./types";
import { Outlet, useOutletContext } from "react-router-dom";
From 4c266494f804ece011e705d61f2891ff6f26c2e7 Mon Sep 17 00:00:00 2001
From: Matt Brophy
Date: Mon, 17 Jul 2023 11:54:01 -0400
Subject: [PATCH 03/12] Add note on creating router outside of the react tree
---
docs/routers/create-browser-router.md | 6 ++++++
docs/routers/router-provider.md | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/docs/routers/create-browser-router.md b/docs/routers/create-browser-router.md
index 27b713da0c..db3f4a554e 100644
--- a/docs/routers/create-browser-router.md
+++ b/docs/routers/create-browser-router.md
@@ -9,6 +9,10 @@ This is the recommended router for all React Router web projects. It uses the [D
It also enables the v6.4 data APIs like [loaders][loader], [actions][action], [fetchers][fetcher] and more.
+
+Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the [Remixing React Router][remixing-react-router] blog post and the W[When to Fetch][when-to-fetch] conference talk.
+
+
```tsx lines=[4,11-24]
import * as React from "react";
import * as ReactDOM from "react-dom";
@@ -125,3 +129,5 @@ Useful for environments like browser devtool plugins or testing to use a differe
[routes]: ../components/routes
[historyapi]: https://developer.mozilla.org/en-US/docs/Web/API/History
[api-development-strategy]: ../guides/api-development-strategy
+[remixing-react-router]: https://remix.run/blog/remixing-react-router
+[when-to-fetch]: https://www.youtube.com/watch?v=95B8mnhzoCM
diff --git a/docs/routers/router-provider.md b/docs/routers/router-provider.md
index 915661b1fb..6d1cac145a 100644
--- a/docs/routers/router-provider.md
+++ b/docs/routers/router-provider.md
@@ -24,6 +24,10 @@ interface RouterProviderProps {
All [data router][picking-a-router] objects are passed to this component to render your app and enable the rest of the data APIs.
+
+Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the [Remixing React Router][remixing-react-router] blog post and the W[When to Fetch][when-to-fetch] conference talk.
+
+
```jsx lines=[24]
import {
createBrowserRouter,
@@ -83,3 +87,5 @@ function App() {
[picking-a-router]: ./picking-a-router
[api-development-strategy]: ../guides/api-development-strategy
+[remixing-react-router]: https://remix.run/blog/remixing-react-router
+[when-to-fetch]: https://www.youtube.com/watch?v=95B8mnhzoCM
From 51044b60d1026b1cf31f59da2bfc999e75b4ef3e Mon Sep 17 00:00:00 2001
From: "remix-cla-bot[bot]"
<92060565+remix-cla-bot[bot]@users.noreply.github.com>
Date: Tue, 18 Jul 2023 20:20:22 +0000
Subject: [PATCH 04/12] chore: sort contributors list
---
contributors.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contributors.yml b/contributors.yml
index b6fb910d14..c3ee77e5d2 100644
--- a/contributors.yml
+++ b/contributors.yml
@@ -86,6 +86,7 @@
- infoxicator
- IsaiStormBlesed
- Isammoc
+- istarkov
- ivanjeremic
- ivanjonas
- Ivanrenes
@@ -224,4 +225,3 @@
- yionr
- yuleicul
- zheng-chuang
-- istarkov
From 4a0811960955d28114d733ea4ff3b7410936f0f2 Mon Sep 17 00:00:00 2001
From: Matt Brophy
Date: Wed, 19 Jul 2023 14:37:07 -0400
Subject: [PATCH 05/12] Update DEVELOPMENT.md
---
DEVELOPMENT.md | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 146ff550d6..ec9be4efc5 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -41,6 +41,7 @@ You may need to make changes to a pre-release prior to publishing a final stable
- Commit the edited pre-release file along with any unpublished changesets, and push the `release-*` branch to GitHub.
- Wait for the release workflow to finish. The Changesets action in the workflow will open a PR that will increment all versions and generate the changelogs for the stable release.
- Review the updated `CHANGELOG` files and make any adjustments necessary.
+ - `find packages -name 'CHANGELOG.md' -mindepth 2 -maxdepth 2 -exec code {} \;`
- We should remove the changelogs for all pre-releases ahead of publishing the stable version.
- [TODO: We should automate this]
- Prepare the GitHub release notes
@@ -48,8 +49,11 @@ You may need to make changes to a pre-release prior to publishing a final stable
- Merge the PR into the `release-*` branch.
- Once the PR is merged, the release workflow will publish the updated packages to npm.
- Once the release is published:
- - merge the `release-*` branch into `main` and push it up to GitHub
- - merge the `release-*` branch into `dev` and push it up to GitHub
+ - Pull the latest `release-*` branch containing the PR you just merged
+ - Merge the `release-*` branch into `main` **using a non-fast-forward merge** and push it up to GitHub
+ - `git checkout main; git merge --no-ff release-next`
+ - Merge the `release-*` branch into `dev` **using a non-fast-forward merge** and push it up to GitHub
+ - `git checkout dev; git merge --no-ff release-next`
- Convert the `react-router@6.x.y` tag to a Release on GitHub with the name `v6.x.y`
### Hotfix releases
From 028129c193f32c4193baf20677a7ecdff04c3950 Mon Sep 17 00:00:00 2001
From: Sinan Bolel <1915925+sbolel@users.noreply.github.com>
Date: Sat, 22 Jul 2023 19:03:53 -0400
Subject: [PATCH 06/12] docs(routers/create-browser-router): fix docs-info
links (#10726)
---
contributors.yml | 1 +
docs/routers/create-browser-router.md | 4 +---
docs/routers/router-provider.md | 4 +---
3 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/contributors.yml b/contributors.yml
index c3ee77e5d2..1c42095bed 100644
--- a/contributors.yml
+++ b/contributors.yml
@@ -181,6 +181,7 @@
- ryanflorence
- ryanhiebert
- sanketshah19
+- sbolel
- scarf005
- senseibarni
- sergiodxa
diff --git a/docs/routers/create-browser-router.md b/docs/routers/create-browser-router.md
index db3f4a554e..9e32bf683f 100644
--- a/docs/routers/create-browser-router.md
+++ b/docs/routers/create-browser-router.md
@@ -9,9 +9,7 @@ This is the recommended router for all React Router web projects. It uses the [D
It also enables the v6.4 data APIs like [loaders][loader], [actions][action], [fetchers][fetcher] and more.
-
-Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the [Remixing React Router][remixing-react-router] blog post and the W[When to Fetch][when-to-fetch] conference talk.
-
+Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the [Remixing React Router][remixing-react-router] blog post and the [When to Fetch][when-to-fetch] conference talk.
```tsx lines=[4,11-24]
import * as React from "react";
diff --git a/docs/routers/router-provider.md b/docs/routers/router-provider.md
index 6d1cac145a..386a821e15 100644
--- a/docs/routers/router-provider.md
+++ b/docs/routers/router-provider.md
@@ -24,9 +24,7 @@ interface RouterProviderProps {
All [data router][picking-a-router] objects are passed to this component to render your app and enable the rest of the data APIs.
-
-Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the [Remixing React Router][remixing-react-router] blog post and the W[When to Fetch][when-to-fetch] conference talk.
-
+Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the [Remixing React Router][remixing-react-router] blog post and the [When to Fetch][when-to-fetch] conference talk.
```jsx lines=[24]
import {
From 1d59f8476b8cc3f2a0688a79d583956cb0299f06 Mon Sep 17 00:00:00 2001
From: Arka Pratim Chaudhuri <105232141+arka1002@users.noreply.github.com>
Date: Sun, 23 Jul 2023 04:34:18 +0530
Subject: [PATCH 07/12] Update use-submit.md, a small link fix in the docs
(#10727)
---
contributors.yml | 1 +
docs/hooks/use-submit.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/contributors.yml b/contributors.yml
index 1c42095bed..0461a5067c 100644
--- a/contributors.yml
+++ b/contributors.yml
@@ -211,6 +211,7 @@
- turansky
- tyankatsu0105
- underager
+- arka1002
- valerii15298
- ValiantCat
- vijaypushkin
diff --git a/docs/hooks/use-submit.md b/docs/hooks/use-submit.md
index f21347d2d2..87da9f7500 100644
--- a/docs/hooks/use-submit.md
+++ b/docs/hooks/use-submit.md
@@ -153,4 +153,4 @@ submit(null, {
Because submissions are navigations, the options may also contain the other navigation related props from [`