react-router 8.4.0 → 8.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -1
- package/dist/development/dom-export.d.ts +2 -1
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +2 -2
- package/dist/development/index-react-server.d.ts +237 -165
- package/dist/development/index-react-server.js +69 -33
- package/dist/development/index.d.ts +9 -8
- package/dist/development/index.js +5 -4
- package/dist/development/lib/actions.js +1 -1
- package/dist/development/lib/await.js +58 -0
- package/dist/development/lib/components.d.ts +41 -5
- package/dist/development/lib/components.js +82 -5
- package/dist/development/lib/context.d.ts +8 -3
- package/dist/development/lib/context.js +1 -1
- package/dist/development/lib/dom/dom.d.ts +3 -0
- package/dist/development/lib/dom/dom.js +1 -1
- package/dist/development/lib/dom/lib.d.ts +34 -9
- package/dist/development/lib/dom/lib.js +17 -6
- package/dist/development/lib/dom/server.js +2 -2
- package/dist/development/lib/dom/ssr/components.js +12 -9
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/entry.d.ts +9 -2
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/development/lib/dom/ssr/fog-of-war.js +25 -13
- package/dist/development/lib/dom/ssr/hydration.js +1 -1
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +1 -1
- package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/development/lib/dom/ssr/routes.js +8 -8
- package/dist/development/lib/dom/ssr/server.js +3 -1
- package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/development/lib/dom/ssr/single-fetch.js +73 -55
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/development/lib/dom-export/hydrated-router.js +32 -12
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.d.ts +3 -6
- package/dist/development/lib/hooks.js +8 -9
- package/dist/development/lib/href.js +1 -1
- package/dist/development/lib/router/history.js +1 -1
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.preload.js +1 -1
- package/dist/development/lib/router/matcher.js +6 -1
- package/dist/development/lib/router/navigation.js +1 -1
- package/dist/development/lib/router/router.d.ts +8 -0
- package/dist/development/lib/router/router.js +213 -91
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.d.ts +31 -4
- package/dist/development/lib/router/utils.js +4 -3
- package/dist/development/lib/rsc/browser.js +4 -4
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +24 -12
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/development/lib/rsc/server.ssr.js +1 -1
- package/dist/development/lib/server-runtime/build.d.ts +14 -0
- package/dist/development/lib/server-runtime/cookies.js +1 -1
- package/dist/development/lib/server-runtime/crypto.js +1 -1
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +1 -1
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +1 -1
- package/dist/development/lib/server-runtime/routes.d.ts +1 -1
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.js +24 -8
- package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions.js +2 -2
- package/dist/development/lib/server-runtime/single-fetch.js +1 -1
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/lib/types/route-data.d.ts +2 -2
- package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/development/route-pattern.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +23 -3
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.d.ts +2 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +2 -2
- package/dist/production/index-react-server.d.ts +237 -165
- package/dist/production/index-react-server.js +69 -33
- package/dist/production/index.d.ts +9 -8
- package/dist/production/index.js +5 -4
- package/dist/production/lib/actions.js +1 -1
- package/dist/production/lib/await.js +58 -0
- package/dist/production/lib/components.d.ts +41 -5
- package/dist/production/lib/components.js +82 -5
- package/dist/production/lib/context.d.ts +8 -3
- package/dist/production/lib/context.js +1 -1
- package/dist/production/lib/dom/dom.d.ts +3 -0
- package/dist/production/lib/dom/dom.js +1 -1
- package/dist/production/lib/dom/lib.d.ts +34 -9
- package/dist/production/lib/dom/lib.js +17 -6
- package/dist/production/lib/dom/server.js +2 -2
- package/dist/production/lib/dom/ssr/components.js +12 -9
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/entry.d.ts +9 -2
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/production/lib/dom/ssr/fog-of-war.js +25 -13
- package/dist/production/lib/dom/ssr/hydration.js +1 -1
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +1 -1
- package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/production/lib/dom/ssr/routes.js +8 -8
- package/dist/production/lib/dom/ssr/server.js +3 -1
- package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/production/lib/dom/ssr/single-fetch.js +73 -55
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/production/lib/dom-export/hydrated-router.js +32 -12
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.d.ts +3 -6
- package/dist/production/lib/hooks.js +7 -8
- package/dist/production/lib/href.js +1 -1
- package/dist/production/lib/router/history.js +1 -1
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.preload.js +1 -1
- package/dist/production/lib/router/matcher.js +6 -1
- package/dist/production/lib/router/navigation.js +1 -1
- package/dist/production/lib/router/router.d.ts +8 -0
- package/dist/production/lib/router/router.js +213 -91
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.d.ts +31 -4
- package/dist/production/lib/router/utils.js +4 -3
- package/dist/production/lib/rsc/browser.js +4 -4
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +24 -12
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/production/lib/rsc/server.ssr.js +1 -1
- package/dist/production/lib/server-runtime/build.d.ts +14 -0
- package/dist/production/lib/server-runtime/cookies.js +1 -1
- package/dist/production/lib/server-runtime/crypto.js +1 -1
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +1 -1
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +1 -1
- package/dist/production/lib/server-runtime/routes.d.ts +1 -1
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.js +24 -8
- package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions.js +2 -2
- package/dist/production/lib/server-runtime/single-fetch.js +1 -1
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/lib/types/route-data.d.ts +2 -2
- package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/production/route-pattern.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +23 -3
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/explanation/backend-for-frontend.md +0 -3
- package/docs/explanation/code-splitting.md +0 -3
- package/docs/explanation/concurrency.md +0 -3
- package/docs/explanation/hot-module-replacement.md +0 -3
- package/docs/explanation/lazy-route-discovery.md +4 -3
- package/docs/explanation/progressive-enhancement.md +0 -3
- package/docs/explanation/race-conditions.md +0 -3
- package/docs/explanation/react-transitions.md +0 -3
- package/docs/explanation/sessions-and-cookies.md +109 -5
- package/docs/explanation/state-management.md +0 -3
- package/docs/explanation/styling.md +0 -3
- package/docs/explanation/type-safety.md +0 -3
- package/docs/how-to/accessibility.md +0 -6
- package/docs/how-to/client-data.md +0 -3
- package/docs/how-to/custom-fetch.md +218 -0
- package/docs/how-to/data-strategy.md +23 -4
- package/docs/how-to/error-boundary.md +0 -3
- package/docs/how-to/error-reporting.md +0 -3
- package/docs/how-to/fetchers.md +0 -3
- package/docs/how-to/file-route-conventions.md +0 -3
- package/docs/how-to/file-uploads.md +0 -3
- package/docs/how-to/form-validation.md +0 -3
- package/docs/how-to/headers.md +0 -3
- package/docs/how-to/instrumentation.md +0 -3
- package/docs/how-to/middleware.md +7 -3
- package/docs/how-to/navigation-blocking.md +0 -3
- package/docs/how-to/optimize-revalidation.md +2 -5
- package/docs/how-to/pre-rendering.md +23 -4
- package/docs/how-to/presets.md +0 -3
- package/docs/how-to/react-server-components.md +0 -3
- package/docs/how-to/resource-routes.md +0 -3
- package/docs/how-to/route-discovery.md +147 -0
- package/docs/how-to/route-module-type-safety.md +0 -3
- package/docs/how-to/security.md +0 -3
- package/docs/how-to/server-bundles.md +0 -3
- package/docs/how-to/spa.md +2 -3
- package/docs/how-to/status.md +0 -3
- package/docs/how-to/suspense.md +22 -6
- package/docs/how-to/using-handle.md +0 -3
- package/docs/how-to/view-transitions.md +79 -15
- package/docs/start/framework/data-loading.md +9 -0
- package/docs/start/framework/navigating.md +5 -9
- package/docs/start/framework/rendering.md +37 -0
- package/docs/upgrading/future.md +0 -12
- package/docs/upgrading/v7.md +3 -33
- package/package.json +9 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.5.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -50,6 +50,7 @@ function hydrate(index) {
|
|
|
50
50
|
set(-0);
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
|
+
validateIndex(index, values.length);
|
|
53
54
|
if (hydrated[index]) {
|
|
54
55
|
set(hydrated[index]);
|
|
55
56
|
continue;
|
|
@@ -121,11 +122,21 @@ function hydrate(index) {
|
|
|
121
122
|
set(obj);
|
|
122
123
|
continue;
|
|
123
124
|
case "P":
|
|
125
|
+
validateIndex(b, values.length);
|
|
124
126
|
if (hydrated[b]) set(hydrated[index] = hydrated[b]);
|
|
125
127
|
else {
|
|
126
128
|
const d = new Deferred();
|
|
129
|
+
const promise = d.promise;
|
|
130
|
+
promise.status = "pending";
|
|
131
|
+
promise.then((value) => {
|
|
132
|
+
promise.status = "fulfilled";
|
|
133
|
+
promise.value = value;
|
|
134
|
+
}, (reason) => {
|
|
135
|
+
promise.status = "rejected";
|
|
136
|
+
promise.reason = reason;
|
|
137
|
+
});
|
|
127
138
|
deferred[b] = d;
|
|
128
|
-
set(hydrated[index] =
|
|
139
|
+
set(hydrated[index] = promise);
|
|
129
140
|
}
|
|
130
141
|
continue;
|
|
131
142
|
case "E":
|
|
@@ -135,6 +146,7 @@ function hydrate(index) {
|
|
|
135
146
|
set(error);
|
|
136
147
|
continue;
|
|
137
148
|
case "Z":
|
|
149
|
+
validateIndex(b, values.length);
|
|
138
150
|
set(hydrated[index] = hydrated[b]);
|
|
139
151
|
continue;
|
|
140
152
|
default:
|
|
@@ -185,7 +197,12 @@ function hydrate(index) {
|
|
|
185
197
|
r[0] = k;
|
|
186
198
|
}]);
|
|
187
199
|
postRun.push(() => {
|
|
188
|
-
object
|
|
200
|
+
Object.defineProperty(object, r[0], {
|
|
201
|
+
value: r[1],
|
|
202
|
+
enumerable: true,
|
|
203
|
+
configurable: true,
|
|
204
|
+
writable: true
|
|
205
|
+
});
|
|
189
206
|
});
|
|
190
207
|
}
|
|
191
208
|
set(object);
|
|
@@ -195,5 +212,8 @@ function hydrate(index) {
|
|
|
195
212
|
while (postRun.length > 0) postRun.pop()();
|
|
196
213
|
return result;
|
|
197
214
|
}
|
|
215
|
+
function validateIndex(index, length) {
|
|
216
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= length) throw new SyntaxError();
|
|
217
|
+
}
|
|
198
218
|
//#endregion
|
|
199
219
|
export { unflatten };
|
|
@@ -6,9 +6,6 @@ title: Backend For Frontend
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
While React Router can serve as your fullstack application, it also fits perfectly into the "Backend for Frontend" architecture.
|
|
13
10
|
|
|
14
11
|
The BFF strategy employs a web server with a job scoped to serving the frontend web app and connecting it to the services it needs: your database, mailer, job queues, existing backend APIs (REST, GraphQL), etc. Instead of your UI integrating directly from the browser to these services, it connects to the BFF, and the BFF connects to your services.
|
|
@@ -6,9 +6,6 @@ title: Automatic Code Splitting
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
When using React Router's framework features, your application is automatically code split to improve the performance of initial load times when users visit your application.
|
|
13
10
|
|
|
14
11
|
## Code Splitting by Route
|
|
@@ -6,9 +6,6 @@ title: Network Concurrency Management
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework, data]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
When building web applications, managing network requests can be a daunting task. The challenges of ensuring up-to-date data and handling simultaneous requests often lead to complex logic in the application to deal with interruptions and race conditions. React Router simplifies this process by automating network management while mirroring and expanding upon the intuitive behavior of web browsers.
|
|
13
10
|
|
|
14
11
|
To help understand how React Router handles concurrency, it's important to remember that after `form` submissions, React Router will fetch fresh data from the `loader`s. This is called revalidation.
|
|
@@ -6,9 +6,6 @@ title: Hot Module Replacement
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
Hot Module Replacement is a technique for updating modules in your app without needing to reload the page.
|
|
13
10
|
It's a great developer experience, and React Router supports it when using Vite.
|
|
14
11
|
|
|
@@ -6,9 +6,6 @@ title: Lazy Route Discovery
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
Lazy Route Discovery is a performance optimization that loads route information progressively as users navigate through your application, rather than loading the complete route manifest upfront.
|
|
13
10
|
|
|
14
11
|
With Lazy Route Discovery enabled (the default), React Router sends only the routes needed for the initial server-side render in the manifest. As users navigate to new parts of your application, additional route information is fetched dynamically and added to the client-side manifest.
|
|
@@ -76,3 +73,7 @@ When using lazy route discovery, ensure your deployment setup handles manifest r
|
|
|
76
73
|
- **Route Handling** - Ensure `/__manifest` requests reach your React Router handler
|
|
77
74
|
- **CDN Caching** - If using CDN/edge caching, include `version` and `paths` query parameters in your cache key for the manifest endpoint
|
|
78
75
|
- **Multiple Applications** - Use a custom `manifestPath` if running multiple React Router applications on the same domain
|
|
76
|
+
|
|
77
|
+
## Application-Controlled Discovery
|
|
78
|
+
|
|
79
|
+
Use the experimental `unstable_useRouteDiscovery` hook to discover selected destinations, complete the route tree after startup, or control manifest-mismatch recovery. See [Application-Controlled Route Discovery](../how-to/route-discovery).
|
|
@@ -6,9 +6,6 @@ title: Progressive Enhancement
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
> Progressive enhancement is a strategy in web design that puts emphasis on web content first, allowing everyone to access the basic content and functionality of a web page, whilst users with additional browser features or faster Internet access receive the enhanced version instead.
|
|
13
10
|
>
|
|
14
11
|
> <cite>- [Wikipedia][wikipedia]</cite>
|
|
@@ -6,9 +6,6 @@ title: Race Conditions
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework, data]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
While impossible to eliminate every possible race condition in your application, React Router automatically handles the most common race conditions found in web user interfaces.
|
|
13
10
|
|
|
14
11
|
## Browser Behavior
|
|
@@ -6,9 +6,6 @@ title: React Transitions
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework, data, declarative]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
[React 18][react-18] introduced the concept of "transitions" which allow you to differentiate urgent from non-urgent UI updates. To learn more about React Transitions and "concurrent rendering" Please refer to React's official documentation:
|
|
13
10
|
|
|
14
11
|
- [What is Concurrent React][concurrent]
|
|
@@ -59,28 +59,130 @@ The input/output to a session storage object are HTTP cookies. `getSession()` re
|
|
|
59
59
|
|
|
60
60
|
You'll use methods to get access to sessions in your `loader` and `action` functions.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
### Session API
|
|
63
|
+
|
|
64
|
+
After retrieving a session with `getSession()`, the returned session object has a handful of methods and properties:
|
|
63
65
|
|
|
64
66
|
```tsx
|
|
65
67
|
export async function action({
|
|
66
68
|
request,
|
|
67
|
-
}:
|
|
69
|
+
}: Route.ActionArgs) {
|
|
68
70
|
const session = await getSession(
|
|
69
71
|
request.headers.get("Cookie"),
|
|
70
72
|
);
|
|
73
|
+
|
|
71
74
|
session.get("foo");
|
|
72
75
|
session.has("bar");
|
|
73
76
|
// etc.
|
|
77
|
+
|
|
78
|
+
return redirect("/", {
|
|
79
|
+
headers: {
|
|
80
|
+
"Set-Cookie": await commitSession(session),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
74
83
|
}
|
|
75
84
|
```
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
<docs-warning>Every time you modify session data, you must `commitSession()` or your changes will be lost. When using cookie session storage, you must also send the `Set-Cookie` header it returns in your response.</docs-warning>
|
|
87
|
+
|
|
88
|
+
See the [Session API][session-api] for all methods and properties available on the session object.
|
|
89
|
+
|
|
90
|
+
#### `session.has(key)`
|
|
91
|
+
|
|
92
|
+
Returns `true` if the session has a value (or an unread flash value) for the given `key`. This does not consume flash values.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
session.has("userId");
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### `session.set(key, value)`
|
|
99
|
+
|
|
100
|
+
Sets a session value for use in subsequent requests:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
session.set("userId", "1234");
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `session.flash(key, value)`
|
|
107
|
+
|
|
108
|
+
Sets a session value that is only valid until the next `session.get()` for that key. After that, it's gone. Most useful for "flash messages" and server-side form validation messages:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
session.flash(
|
|
112
|
+
"globalMessage",
|
|
113
|
+
"Project successfully archived",
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A flash value is typically set in an `action` before a redirect, then read in the `loader` of the page being redirected to:
|
|
118
|
+
|
|
119
|
+
```tsx filename=app/routes/projects.tsx
|
|
120
|
+
export async function action({
|
|
121
|
+
request,
|
|
122
|
+
}: Route.ActionArgs) {
|
|
123
|
+
const session = await getSession(
|
|
124
|
+
request.headers.get("Cookie"),
|
|
125
|
+
);
|
|
126
|
+
// ...archive the project
|
|
127
|
+
session.flash(
|
|
128
|
+
"globalMessage",
|
|
129
|
+
"Project successfully archived",
|
|
130
|
+
);
|
|
131
|
+
return redirect("/projects", {
|
|
132
|
+
headers: {
|
|
133
|
+
"Set-Cookie": await commitSession(session),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function loader({
|
|
139
|
+
request,
|
|
140
|
+
}: Route.LoaderArgs) {
|
|
141
|
+
const session = await getSession(
|
|
142
|
+
request.headers.get("Cookie"),
|
|
143
|
+
);
|
|
144
|
+
// Reading a flash value removes it from the session
|
|
145
|
+
const message = session.get("globalMessage");
|
|
146
|
+
|
|
147
|
+
return data(
|
|
148
|
+
{ message },
|
|
149
|
+
{
|
|
150
|
+
headers: {
|
|
151
|
+
// Commit the session so the flash value is removed
|
|
152
|
+
"Set-Cookie": await commitSession(session),
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Flash values are read with `session.get()` using the same key they were set with. Reading a flash value removes it from the session, so you must still `commitSession()` after reading it; otherwise the flash value will be read again on the next request.
|
|
160
|
+
|
|
161
|
+
Avoid using the same key for both `session.set()` and `session.flash()`. `session.get()` returns the regular value first, so the flash value would never be read or removed.
|
|
162
|
+
|
|
163
|
+
You can type flash values separately from regular session values with the second generic on your session storage, like the `SessionFlashData` type in [Using Sessions](#using-sessions).
|
|
164
|
+
|
|
165
|
+
#### `session.get(key)`
|
|
166
|
+
|
|
167
|
+
Accesses a session value from a previous request:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
session.get("name");
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `session.unset(key)`
|
|
174
|
+
|
|
175
|
+
Removes a value from the session. This does not remove pending flash values; those are only removed when read with `session.get()`.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
session.unset("name");
|
|
179
|
+
```
|
|
78
180
|
|
|
79
181
|
### Login form example
|
|
80
182
|
|
|
81
183
|
A login form might look something like this:
|
|
82
184
|
|
|
83
|
-
```tsx filename=app/routes/login.tsx lines=[4-7,12-14,16,22,
|
|
185
|
+
```tsx filename=app/routes/login.tsx lines=[4-7,12-14,16,22-23,26,35-37,48-49,54,59,64]
|
|
84
186
|
import { data, redirect } from "react-router";
|
|
85
187
|
import type { Route } from "./+types/login";
|
|
86
188
|
|
|
@@ -102,6 +204,7 @@ export async function loader({
|
|
|
102
204
|
}
|
|
103
205
|
|
|
104
206
|
return data(
|
|
207
|
+
// Read and unset the flash message set by the route action.
|
|
105
208
|
{ error: session.get("error") },
|
|
106
209
|
{
|
|
107
210
|
headers: {
|
|
@@ -127,6 +230,7 @@ export async function action({
|
|
|
127
230
|
);
|
|
128
231
|
|
|
129
232
|
if (userId == null) {
|
|
233
|
+
// Set a single-use flash message to be read by the route loader.
|
|
130
234
|
session.flash("error", "Invalid username/password");
|
|
131
235
|
|
|
132
236
|
// Redirect back to the login page with errors.
|
|
@@ -220,7 +324,7 @@ React Router makes it easy to store sessions in your own database if needed. The
|
|
|
220
324
|
- `createData` will be called from `commitSession` on the initial session creation when no session ID exists in the cookie
|
|
221
325
|
- `readData` will be called from `getSession` when a session ID exists in the cookie
|
|
222
326
|
- `updateData` will be called from `commitSession` when a session ID already exists in the cookie
|
|
223
|
-
- `deleteData` is called from `destroySession`
|
|
327
|
+
- `deleteData` is called from `destroySession` when the session has an ID
|
|
224
328
|
|
|
225
329
|
The following example shows how you could do this using a generic database client:
|
|
226
330
|
|
|
@@ -6,9 +6,6 @@ title: State Management
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework, data]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
State management in React typically involves maintaining a synchronized cache of server data on the client side. However, when using React Router as your framework, most of the traditional caching solutions become redundant because of how it inherently handles data synchronization.
|
|
13
10
|
|
|
14
11
|
## Understanding State Management in React
|
|
@@ -6,9 +6,6 @@ title: Styling
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
Framework mode uses the React Router Vite plugin, so the styling story is mostly just Vite's styling story.
|
|
13
10
|
|
|
14
11
|
React Router does not have a separate CSS pipeline for Framework mode. In practice, there are three patterns that matter:
|
|
@@ -6,9 +6,6 @@ title: Type Safety
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
If you haven't done so already, check out our guide for [setting up type safety][route-module-type-safety] in a new project.
|
|
13
10
|
|
|
14
11
|
React Router generates types for each route in your app to provide type safety for the route module exports.
|
|
@@ -12,9 +12,6 @@ React Router makes certain accessibility practices the default where possible an
|
|
|
12
12
|
|
|
13
13
|
[MODES: framework, data, declarative]
|
|
14
14
|
|
|
15
|
-
<br/>
|
|
16
|
-
<br/>
|
|
17
|
-
|
|
18
15
|
The [`<Link>` component][link] renders a standard anchor tag, meaning that you get its accessibility behaviors from the browser for free!
|
|
19
16
|
|
|
20
17
|
React Router also provides the [`<NavLink/>`][navlink] which behaves the same as `<Link>`, but it also provides context for assistive technology when the link points to the current page. This is useful for building navigation menus or breadcrumbs.
|
|
@@ -23,9 +20,6 @@ React Router also provides the [`<NavLink/>`][navlink] which behaves the same as
|
|
|
23
20
|
|
|
24
21
|
[MODES: framework]
|
|
25
22
|
|
|
26
|
-
<br/>
|
|
27
|
-
<br/>
|
|
28
|
-
|
|
29
23
|
If you are rendering [`<Scripts>`][scripts] in your app, there are some important things to consider to make client-side routing more accessible for your users.
|
|
30
24
|
|
|
31
25
|
With a traditional multi-page website we don't have to think about route changes too much. Your app renders an anchor tag, and the browser handles the rest. If your users disable JavaScript, your React Router app should already work this way by default!
|
|
@@ -6,9 +6,6 @@ title: Client Data
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
You can fetch and mutate data directly in the browser using `clientLoader` and `clientAction` functions.
|
|
13
10
|
|
|
14
11
|
These functions are the primary mechanism for data handling when using [SPA mode][spa]. This guide demonstrates common use cases for leveraging client data in Server-Side Rendering (SSR).
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Custom Fetch
|
|
3
|
+
unstable: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Custom Fetch
|
|
7
|
+
|
|
8
|
+
[MODES: framework]
|
|
9
|
+
|
|
10
|
+
<docs-warning>This feature is experimental and may change in minor or patch releases. It is not supported in RSC Framework Mode.</docs-warning>
|
|
11
|
+
|
|
12
|
+
The `unstable_fetch` prop on [`HydratedRouter`](../api/framework-routers/HydratedRouter) lets you customize the JavaScript-issued requests React Router makes for server loader/action data and lazy route discovery. Use it to add headers, customize fetch options, or apply a request policy based on the operation that initiated the request.
|
|
13
|
+
|
|
14
|
+
This is a Framework Mode API. In Data Mode, you control fetching in your loaders/actions or through a [`dataStrategy`](./data-strategy#request-metadata-unstable).
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
If you don't have an [`app/entry.client.tsx`](../api/framework-conventions/entry.client.tsx), reveal the default entry:
|
|
19
|
+
|
|
20
|
+
```shellscript nonumber
|
|
21
|
+
npx react-router reveal entry.client
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Pass a custom implementation to `HydratedRouter`:
|
|
25
|
+
|
|
26
|
+
```tsx filename=app/entry.client.tsx
|
|
27
|
+
import { startTransition, StrictMode } from "react";
|
|
28
|
+
import { hydrateRoot } from "react-dom/client";
|
|
29
|
+
import {
|
|
30
|
+
HydratedRouter,
|
|
31
|
+
type unstable_RouterFetch,
|
|
32
|
+
} from "react-router/dom";
|
|
33
|
+
|
|
34
|
+
const customFetch: unstable_RouterFetch = (
|
|
35
|
+
request,
|
|
36
|
+
context,
|
|
37
|
+
) => {
|
|
38
|
+
console.log(context.type, request.method, request.url);
|
|
39
|
+
return fetch(request);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
startTransition(() => {
|
|
43
|
+
hydrateRoot(
|
|
44
|
+
document,
|
|
45
|
+
<StrictMode>
|
|
46
|
+
<HydratedRouter unstable_fetch={customFetch} />
|
|
47
|
+
</StrictMode>,
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The callback receives a constructed `Request`, including its URL, headers, body, and abort signal, and must return a `Promise<Response>`. Pass that request to the browser's `fetch` to preserve those settings, including cancellation.
|
|
53
|
+
|
|
54
|
+
`HydratedRouter` creates a singleton router and reads `unstable_fetch` only during initialization. Changing the prop later has no effect. If your implementation needs changing values such as authentication tokens, read current application state inside the callback instead of capturing values from a component render.
|
|
55
|
+
|
|
56
|
+
## Request Context
|
|
57
|
+
|
|
58
|
+
The [`unstable_RouterFetchContext`](https://api.reactrouter.com/v8/types/react-router.dom.unstable_RouterFetchContext.html) type is exported from `react-router/dom` and has the following shape:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import type { NavigationType } from "react-router";
|
|
62
|
+
|
|
63
|
+
type unstable_RouterFetchContext =
|
|
64
|
+
| { type: "manifest" }
|
|
65
|
+
| {
|
|
66
|
+
type: "navigation";
|
|
67
|
+
fetcherKey: string | null;
|
|
68
|
+
navigationType: NavigationType;
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
type: "initialization" | "fetcher" | "revalidation";
|
|
72
|
+
fetcherKey: string | null;
|
|
73
|
+
navigationType: null;
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`type` identifies the operation that initiated the request:
|
|
78
|
+
|
|
79
|
+
- `"manifest"`: Lazy route discovery requests. These have no `fetcherKey` or `navigationType`.
|
|
80
|
+
- `"initialization"`: Data loading during router initialization, such as initial loader requests in API-only Mode.
|
|
81
|
+
- `"navigation"`: Data requests initiated by a navigation, including action submissions and the subsequent loader revalidation.
|
|
82
|
+
- `"fetcher"`: Requests initiated by a fetcher, including the loader revalidation after a fetcher action.
|
|
83
|
+
- `"revalidation"`: Requests initiated by an explicit revalidation, such as `useRevalidator().revalidate()`.
|
|
84
|
+
|
|
85
|
+
`fetcherKey` identifies the fetcher targeted by a data request, or is `null` for route loader/action requests. A fetcher reloaded as part of a navigation or explicit revalidation retains that initiating `type` and also includes its fetcher key. Fetchers reloaded after an initialization redirect likewise retain the `"initialization"` type and include their fetcher key.
|
|
86
|
+
|
|
87
|
+
`navigationType` is `"PUSH"`, `"REPLACE"`, or `"POP"` for navigation-initiated data requests, including fetchers reloaded by that navigation. It is `null` for initialization, standalone fetcher operations, and explicit revalidation. This is the history action for the in-flight navigation, not the previously committed location.
|
|
88
|
+
|
|
89
|
+
## Examples
|
|
90
|
+
|
|
91
|
+
Each example below can be passed to `HydratedRouter` as its `unstable_fetch` prop.
|
|
92
|
+
|
|
93
|
+
### Authentication and Custom Headers
|
|
94
|
+
|
|
95
|
+
Read the current token at request time so token changes don't require replacing the callback:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import type { unstable_RouterFetch } from "react-router/dom";
|
|
99
|
+
import { authStore } from "./auth";
|
|
100
|
+
|
|
101
|
+
const customFetch: unstable_RouterFetch = (request) => {
|
|
102
|
+
let headers = new Headers(request.headers);
|
|
103
|
+
let token = authStore.getAccessToken();
|
|
104
|
+
|
|
105
|
+
if (token) {
|
|
106
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
headers.set("X-App-Version", "1.0");
|
|
110
|
+
return fetch(request, { headers });
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Here, `authStore` is your application's authentication store. Only send credentials to trusted origins. Adding custom headers to cross-origin requests may require a CORS preflight and corresponding server configuration.
|
|
115
|
+
|
|
116
|
+
### Cross-Origin Credentials
|
|
117
|
+
|
|
118
|
+
In [API-only Mode](../start/framework/rendering#using-a-separate-api-origin), use `unstable_apiServerOrigin` to configure a separate API origin. React Router applies that origin before calling your custom fetch, and also uses it for native data prefetches.
|
|
119
|
+
|
|
120
|
+
The origin setting does not enable cross-origin cookies. If you need them, customize the credential policy for the trusted API origin:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import type { unstable_RouterFetch } from "react-router/dom";
|
|
124
|
+
|
|
125
|
+
const customFetch: unstable_RouterFetch = (request) => {
|
|
126
|
+
let isApiRequest =
|
|
127
|
+
new URL(request.url).origin ===
|
|
128
|
+
"https://api.example.com";
|
|
129
|
+
|
|
130
|
+
return fetch(request, {
|
|
131
|
+
credentials: isApiRequest
|
|
132
|
+
? "include"
|
|
133
|
+
: request.credentials,
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The API server must allow credentialed CORS requests from your client origin. Browser cookie policies still apply. Native data prefetches do not use this callback, so don't rely on it to configure their credentials.
|
|
139
|
+
|
|
140
|
+
### Back/Forward Traversal Cache
|
|
141
|
+
|
|
142
|
+
Use `navigationType` to prefer the browser's HTTP cache when navigating back or forward, while retaining the normal cache policy for other requests:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import type { unstable_RouterFetch } from "react-router/dom";
|
|
146
|
+
|
|
147
|
+
const customFetch: unstable_RouterFetch = (
|
|
148
|
+
request,
|
|
149
|
+
context,
|
|
150
|
+
) => {
|
|
151
|
+
let isTraversal =
|
|
152
|
+
import.meta.env.PROD &&
|
|
153
|
+
request.method === "GET" &&
|
|
154
|
+
context.type === "navigation" &&
|
|
155
|
+
context.navigationType === "POP";
|
|
156
|
+
|
|
157
|
+
return fetch(request, {
|
|
158
|
+
cache: isTraversal ? "force-cache" : "default",
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This example enables the policy in production only. It includes GET requests for fetchers reloaded by a back/forward navigation, but not standalone fetcher loads or explicit revalidations.
|
|
164
|
+
|
|
165
|
+
The [`force-cache` policy](https://fetch.spec.whatwg.org/#concept-request-cache-mode) uses a matching HTTP cache entry even if it is stale, falling back to the network when no entry exists. Your server's responses must be cacheable; `Cache-Control: no-store` responses aren't stored.
|
|
166
|
+
|
|
167
|
+
This is not a router-managed data cache. React Router still performs its request and response decoding, and this policy adds no mutation or authentication invalidation guarantees. Only use it when serving potentially stale data is appropriate for your application.
|
|
168
|
+
|
|
169
|
+
### Retrying Safe Requests
|
|
170
|
+
|
|
171
|
+
You can retry a safe GET after a network failure without retrying action submissions or canceled requests:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import type { unstable_RouterFetch } from "react-router/dom";
|
|
175
|
+
|
|
176
|
+
const customFetch: unstable_RouterFetch = async (
|
|
177
|
+
request,
|
|
178
|
+
) => {
|
|
179
|
+
if (request.method !== "GET") {
|
|
180
|
+
return fetch(request);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
return await fetch(request.clone());
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (
|
|
187
|
+
request.signal.aborted ||
|
|
188
|
+
(error instanceof Error &&
|
|
189
|
+
error.name === "AbortError")
|
|
190
|
+
) {
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Retry once, retaining the original abort signal.
|
|
195
|
+
return fetch(request);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
This assumes your GET endpoints are safe to repeat. It retries a rejected fetch, not HTTP error responses such as `500`. Don't automatically retry mutations: the server may have processed an action even when the client didn't receive its response.
|
|
201
|
+
|
|
202
|
+
## Limitations
|
|
203
|
+
|
|
204
|
+
### Requests Outside Custom Fetch
|
|
205
|
+
|
|
206
|
+
Custom fetch does not intercept the initial document request, server-side loader execution, static assets, or fetches you write in your own application code, including in `clientLoader`/`clientAction`. Calls to their `serverLoader`/`serverAction` helpers do use custom fetch when they issue server data requests.
|
|
207
|
+
|
|
208
|
+
Data prefetches from `<Link prefetch>`, `<NavLink prefetch>`, and [`PrefetchPageLinks`](../api/components/PrefetchPageLinks) use native `<link rel="prefetch">` elements and bypass custom fetch. If a request requires custom headers or other transformations, use `prefetch="none"` on links and avoid rendering `PrefetchPageLinks` for those routes.
|
|
209
|
+
|
|
210
|
+
### Inspecting Request Bodies
|
|
211
|
+
|
|
212
|
+
If you need a form field such as `intent` to choose request headers, read it from `await request.clone().formData()` for form-encoded submissions, then send the original request. Reading `request.formData()` directly consumes the body so it can't subsequently be sent unchanged.
|
|
213
|
+
|
|
214
|
+
Only parse bodies with a compatible encoding. Cloning and reading a body can buffer data, so avoid doing this unnecessarily, especially for large file uploads. Adding headers or changing fetch options does not require reading the body.
|
|
215
|
+
|
|
216
|
+
### Response Compatibility
|
|
217
|
+
|
|
218
|
+
Return the response from `fetch` unchanged. You can inspect its status and headers, but you should not consume its body or try to decode, modify, or re-encode it. React Router handles response decoding and expects its manifest/data protocol, including Turbo Stream-encoded loader/action data.
|
|
@@ -6,9 +6,6 @@ title: Data Strategy
|
|
|
6
6
|
|
|
7
7
|
[MODES: data]
|
|
8
8
|
|
|
9
|
-
<br />
|
|
10
|
-
<br />
|
|
11
|
-
|
|
12
9
|
<docs-warning>This is a low-level API intended for advanced use-cases. This overrides React Router's internal handling of `action`/`loader` execution, and if done incorrectly will break your app code. Please use with caution and perform the appropriate testing.</docs-warning>
|
|
13
10
|
|
|
14
11
|
## Overview
|
|
@@ -23,7 +20,9 @@ A custom `dataStrategy` receives the `loader`/`action` arguments (`request`, `pa
|
|
|
23
20
|
|
|
24
21
|
- `matches`: An array of `DataStrategyMatch` instances for the routes matched by the current `request`
|
|
25
22
|
- `runClientMiddleware`: A helper function to run the middleware for the matched routes
|
|
26
|
-
- `fetcherKey`: The fetcher
|
|
23
|
+
- `fetcherKey`: The key of the fetcher targeted by this execution, or `null` for route loader/action executions. Fetchers reloaded by a navigation or explicit revalidation also provide their key.
|
|
24
|
+
- `unstable_initiator`: The operation that initiated this execution (see [Request Metadata](#request-metadata-unstable))
|
|
25
|
+
- `unstable_navigationType`: The history action for a navigation-initiated execution, or `null` for other executions
|
|
27
26
|
|
|
28
27
|
A `DataStrategyMatch` is a normal route match plus a few additional fields:
|
|
29
28
|
|
|
@@ -77,6 +76,26 @@ interface DataStrategyResult {
|
|
|
77
76
|
}
|
|
78
77
|
```
|
|
79
78
|
|
|
79
|
+
### Request Metadata (unstable)
|
|
80
|
+
|
|
81
|
+
<docs-warning>`unstable_initiator` and `unstable_navigationType` are experimental and may change in minor or patch releases.</docs-warning>
|
|
82
|
+
|
|
83
|
+
`unstable_initiator` identifies why the data strategy is running:
|
|
84
|
+
|
|
85
|
+
- `"initialization"`: Initial router data loading
|
|
86
|
+
- `"navigation"`: A navigation, including action submissions and subsequent loader revalidation
|
|
87
|
+
- `"fetcher"`: A fetcher operation, including loader revalidation after a fetcher action
|
|
88
|
+
- `"revalidation"`: Explicit revalidation, such as `useRevalidator().revalidate()`
|
|
89
|
+
- `"static"`: Execution by `createStaticHandler` on the server
|
|
90
|
+
|
|
91
|
+
Requests that reload fetchers during a navigation or explicit revalidation retain that initiator while providing the targeted `fetcherKey`. Don't use `fetcherKey` alone to determine what initiated the execution.
|
|
92
|
+
|
|
93
|
+
`unstable_navigationType` is `"PUSH"`, `"REPLACE"`, or `"POP"` for navigation-initiated executions, including fetchers reloaded by that navigation. It is `null` for initialization, standalone fetcher operations, explicit revalidation, and static handler executions. It describes the in-flight navigation rather than the previously committed location.
|
|
94
|
+
|
|
95
|
+
You can use this metadata to choose a loading or caching policy. For example, a Data Mode `dataStrategy` can distinguish back/forward navigations with `unstable_initiator === "navigation" && unstable_navigationType === "POP"`. Your strategy remains responsible for executing the appropriate handlers and returning their results.
|
|
96
|
+
|
|
97
|
+
Framework Mode exposes equivalent request metadata through [`unstable_fetch`](./custom-fetch#request-context), which customizes server requests without replacing loader/action execution.
|
|
98
|
+
|
|
80
99
|
### Calling Route Middleware
|
|
81
100
|
|
|
82
101
|
If you are using `middleware` on your routes, you need to leverage the `callClientMiddleware` helper function to execute `middleware` around your handlers:
|
|
@@ -6,9 +6,6 @@ title: Error Boundaries
|
|
|
6
6
|
|
|
7
7
|
[MODES: framework, data]
|
|
8
8
|
|
|
9
|
-
<br/>
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
9
|
To avoid rendering an empty page to users, route modules will automatically catch errors in your code and render the closest `ErrorBoundary`.
|
|
13
10
|
|
|
14
11
|
Error boundaries are not intended for rendering form validation errors or error reporting. Please see [Form Validation](./form-validation) and [Error Reporting](./error-reporting) instead.
|