react-router-dom 6.18.0 → 6.19.0-pre.0
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 +16 -0
- package/dist/dom.d.ts +4 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +89 -25
- package/dist/index.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/react-router-dom.development.js +95 -25
- package/dist/react-router-dom.development.js.map +1 -1
- package/dist/react-router-dom.production.min.js +2 -2
- package/dist/react-router-dom.production.min.js.map +1 -1
- package/dist/umd/react-router-dom.development.js +101 -31
- package/dist/umd/react-router-dom.development.js.map +1 -1
- package/dist/umd/react-router-dom.production.min.js +2 -2
- package/dist/umd/react-router-dom.production.min.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# `react-router-dom`
|
|
2
2
|
|
|
3
|
+
## 6.19.0-pre.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `unstable_flushSync` option to `useNavigate`/`useSumbit`/`fetcher.load`/`fetcher.submit` to opt-out of `React.startTransition` and into `ReactDOM.flushSync` for state updates ([#11005](https://github.com/remix-run/react-router/pull/11005))
|
|
8
|
+
- Allow `unstable_usePrompt` to accept a `BlockerFunction` in addition to a `boolean` ([#10991](https://github.com/remix-run/react-router/pull/10991))
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Fix issue where a changing fetcher `key` in a `useFetcher` that remains mounted wasn't getting picked up ([#11009](https://github.com/remix-run/react-router/pull/11009))
|
|
13
|
+
- Fix `useFormAction` which was incorrectly inheriting the `?index` query param from child route `action` submissions ([#11025](https://github.com/remix-run/react-router/pull/11025))
|
|
14
|
+
- Fix `NavLink` `active` logic when `to` location has a trailing slash ([#10734](https://github.com/remix-run/react-router/pull/10734))
|
|
15
|
+
- Updated dependencies:
|
|
16
|
+
- `react-router@6.19.0-pre.0`
|
|
17
|
+
- `@remix-run/router@1.12.0-pre.0`
|
|
18
|
+
|
|
3
19
|
## 6.18.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/dist/dom.d.ts
CHANGED
|
@@ -85,6 +85,10 @@ export interface SubmitOptions {
|
|
|
85
85
|
* navigation when using the <ScrollRestoration> component
|
|
86
86
|
*/
|
|
87
87
|
preventScrollReset?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Enable flushSync for this navigation's state updates
|
|
90
|
+
*/
|
|
91
|
+
unstable_flushSync?: boolean;
|
|
88
92
|
/**
|
|
89
93
|
* Enable view transitions on this submission navigation
|
|
90
94
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import type { FutureConfig, Location, NavigateOptions, RelativeRoutingType, RouteObject, RouterProviderProps, To } from "react-router";
|
|
7
|
-
import type { Fetcher, FormEncType, FormMethod, FutureConfig as RouterFutureConfig, GetScrollRestorationKeyFunction, History, HTMLFormMethod, HydrationState, Router as RemixRouter, V7_FormMethod } from "@remix-run/router";
|
|
7
|
+
import type { Fetcher, FormEncType, FormMethod, FutureConfig as RouterFutureConfig, GetScrollRestorationKeyFunction, History, HTMLFormMethod, HydrationState, Router as RemixRouter, V7_FormMethod, BlockerFunction } from "@remix-run/router";
|
|
8
8
|
import type { SubmitOptions, ParamKeyValuePair, URLSearchParamsInit, SubmitTarget } from "./dom";
|
|
9
9
|
import { createSearchParams } from "./dom";
|
|
10
10
|
export type { FormEncType, FormMethod, GetScrollRestorationKeyFunction, ParamKeyValuePair, SubmitOptions, URLSearchParamsInit, V7_FormMethod, };
|
|
11
11
|
export { createSearchParams };
|
|
12
12
|
export type { ActionFunction, ActionFunctionArgs, AwaitProps, unstable_Blocker, unstable_BlockerFunction, DataRouteMatch, DataRouteObject, ErrorResponse, Fetcher, Hash, IndexRouteObject, IndexRouteProps, JsonFunction, LazyRouteFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, Location, MemoryRouterProps, NavigateFunction, NavigateOptions, NavigateProps, Navigation, Navigator, NonIndexRouteObject, OutletProps, Params, ParamParseKey, Path, PathMatch, Pathname, PathPattern, PathRouteProps, RedirectFunction, RelativeRoutingType, RouteMatch, RouteObject, RouteProps, RouterProps, RouterProviderProps, RoutesProps, Search, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs, To, UIMatch, } from "react-router";
|
|
13
|
-
export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, isRouteErrorResponse, generatePath, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, renderMatches, resolvePath, useActionData, useAsyncError, useAsyncValue,
|
|
13
|
+
export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, isRouteErrorResponse, generatePath, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, renderMatches, resolvePath, useActionData, useAsyncError, useAsyncValue, useBlocker, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes, } from "react-router";
|
|
14
14
|
/** @internal */
|
|
15
15
|
export { UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_NavigationContext, UNSAFE_LocationContext, UNSAFE_RouteContext, UNSAFE_useRouteId, } from "react-router";
|
|
16
16
|
declare global {
|
|
@@ -31,6 +31,7 @@ type ViewTransitionContextObject = {
|
|
|
31
31
|
isTransitioning: false;
|
|
32
32
|
} | {
|
|
33
33
|
isTransitioning: true;
|
|
34
|
+
flushSync: boolean;
|
|
34
35
|
currentLocation: Location;
|
|
35
36
|
nextLocation: Location;
|
|
36
37
|
};
|
|
@@ -111,7 +112,6 @@ export interface NavLinkProps extends Omit<LinkProps, "className" | "style" | "c
|
|
|
111
112
|
className?: string | ((props: NavLinkRenderProps) => string | undefined);
|
|
112
113
|
end?: boolean;
|
|
113
114
|
style?: React.CSSProperties | ((props: NavLinkRenderProps) => React.CSSProperties | undefined);
|
|
114
|
-
unstable_viewTransition?: boolean;
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
117
|
* A `<Link>` wrapper that knows if it's "active" or not.
|
|
@@ -252,7 +252,9 @@ export declare function useFormAction(action?: string, { relative }?: {
|
|
|
252
252
|
export type FetcherWithComponents<TData> = Fetcher<TData> & {
|
|
253
253
|
Form: React.ForwardRefExoticComponent<FetcherFormProps & React.RefAttributes<HTMLFormElement>>;
|
|
254
254
|
submit: FetcherSubmitFunction;
|
|
255
|
-
load: (href: string
|
|
255
|
+
load: (href: string, opts?: {
|
|
256
|
+
unstable_flushSync?: boolean;
|
|
257
|
+
}) => void;
|
|
256
258
|
};
|
|
257
259
|
/**
|
|
258
260
|
* Interacts with route loaders and actions without causing a navigation. Great
|
|
@@ -295,8 +297,8 @@ export declare function useBeforeUnload(callback: (event: BeforeUnloadEvent) =>
|
|
|
295
297
|
* very incorrectly in some cases) across browsers if user click addition
|
|
296
298
|
* back/forward navigations while the confirm is open. Use at your own risk.
|
|
297
299
|
*/
|
|
298
|
-
declare function usePrompt({ when, message }: {
|
|
299
|
-
when: boolean;
|
|
300
|
+
declare function usePrompt({ when, message, }: {
|
|
301
|
+
when: boolean | BlockerFunction;
|
|
300
302
|
message: string;
|
|
301
303
|
}): void;
|
|
302
304
|
export { usePrompt as unstable_usePrompt };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* React Router DOM v6.
|
|
2
|
+
* React Router DOM v6.19.0-pre.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
11
|
import * as React from 'react';
|
|
12
|
-
import
|
|
13
|
-
|
|
12
|
+
import * as ReactDOM from 'react-dom';
|
|
13
|
+
import { UNSAFE_mapRouteProperties, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, Router, UNSAFE_useRoutesImpl, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, useBlocker } from 'react-router';
|
|
14
|
+
export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_LocationContext, UNSAFE_NavigationContext, UNSAFE_RouteContext, UNSAFE_useRouteId, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, renderMatches, resolvePath, useActionData, useAsyncError, useAsyncValue, useBlocker, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes } from 'react-router';
|
|
14
15
|
import { stripBasename, UNSAFE_warning, createRouter, createBrowserHistory, createHashHistory, UNSAFE_ErrorResponseImpl, UNSAFE_invariant, joinPaths, IDLE_FETCHER, matchPath } from '@remix-run/router';
|
|
15
16
|
|
|
16
17
|
function _extends() {
|
|
@@ -325,6 +326,8 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
325
326
|
*/
|
|
326
327
|
const START_TRANSITION = "startTransition";
|
|
327
328
|
const startTransitionImpl = React[START_TRANSITION];
|
|
329
|
+
const FLUSH_SYNC = "flushSync";
|
|
330
|
+
const flushSyncImpl = ReactDOM[FLUSH_SYNC];
|
|
328
331
|
function startTransitionSafe(cb) {
|
|
329
332
|
if (startTransitionImpl) {
|
|
330
333
|
startTransitionImpl(cb);
|
|
@@ -332,6 +335,13 @@ function startTransitionSafe(cb) {
|
|
|
332
335
|
cb();
|
|
333
336
|
}
|
|
334
337
|
}
|
|
338
|
+
function flushSyncSafe(cb) {
|
|
339
|
+
if (flushSyncImpl) {
|
|
340
|
+
flushSyncImpl(cb);
|
|
341
|
+
} else {
|
|
342
|
+
cb();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
335
345
|
class Deferred {
|
|
336
346
|
constructor() {
|
|
337
347
|
this.status = "pending";
|
|
@@ -382,6 +392,7 @@ function RouterProvider(_ref) {
|
|
|
382
392
|
let setState = React.useCallback((newState, _ref2) => {
|
|
383
393
|
let {
|
|
384
394
|
deletedFetchers,
|
|
395
|
+
unstable_flushSync: flushSync,
|
|
385
396
|
unstable_viewTransitionOpts: viewTransitionOpts
|
|
386
397
|
} = _ref2;
|
|
387
398
|
deletedFetchers.forEach(key => fetcherData.current.delete(key));
|
|
@@ -390,13 +401,56 @@ function RouterProvider(_ref) {
|
|
|
390
401
|
fetcherData.current.set(key, fetcher.data);
|
|
391
402
|
}
|
|
392
403
|
});
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
404
|
+
let isViewTransitionUnavailable = router.window == null || typeof router.window.document.startViewTransition !== "function";
|
|
405
|
+
// If this isn't a view transition or it's not available in this browser,
|
|
406
|
+
// just update and be done with it
|
|
407
|
+
if (!viewTransitionOpts || isViewTransitionUnavailable) {
|
|
408
|
+
if (flushSync) {
|
|
409
|
+
flushSyncSafe(() => setStateImpl(newState));
|
|
410
|
+
} else {
|
|
411
|
+
optInStartTransition(() => setStateImpl(newState));
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
// flushSync + startViewTransition
|
|
416
|
+
if (flushSync) {
|
|
417
|
+
// Flush through the context to mark DOM elements as transition=ing
|
|
418
|
+
flushSyncSafe(() => {
|
|
419
|
+
// Cancel any pending transitions
|
|
420
|
+
if (transition) {
|
|
421
|
+
renderDfd && renderDfd.resolve();
|
|
422
|
+
transition.skipTransition();
|
|
423
|
+
}
|
|
424
|
+
setVtContext({
|
|
425
|
+
isTransitioning: true,
|
|
426
|
+
flushSync: true,
|
|
427
|
+
currentLocation: viewTransitionOpts.currentLocation,
|
|
428
|
+
nextLocation: viewTransitionOpts.nextLocation
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
// Update the DOM
|
|
432
|
+
let t = router.window.document.startViewTransition(() => {
|
|
433
|
+
flushSyncSafe(() => setStateImpl(newState));
|
|
434
|
+
});
|
|
435
|
+
// Clean up after the animation completes
|
|
436
|
+
t.finished.finally(() => {
|
|
437
|
+
flushSyncSafe(() => {
|
|
438
|
+
setRenderDfd(undefined);
|
|
439
|
+
setTransition(undefined);
|
|
440
|
+
setPendingState(undefined);
|
|
441
|
+
setVtContext({
|
|
442
|
+
isTransitioning: false
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
flushSyncSafe(() => setTransition(t));
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
// startTransition + startViewTransition
|
|
450
|
+
if (transition) {
|
|
397
451
|
// Interrupting an in-progress transition, cancel and let everything flush
|
|
398
452
|
// out, and then kick off a new transition from the interruption state
|
|
399
|
-
renderDfd.resolve();
|
|
453
|
+
renderDfd && renderDfd.resolve();
|
|
400
454
|
transition.skipTransition();
|
|
401
455
|
setInterruption({
|
|
402
456
|
state: newState,
|
|
@@ -408,6 +462,7 @@ function RouterProvider(_ref) {
|
|
|
408
462
|
setPendingState(newState);
|
|
409
463
|
setVtContext({
|
|
410
464
|
isTransitioning: true,
|
|
465
|
+
flushSync: false,
|
|
411
466
|
currentLocation: viewTransitionOpts.currentLocation,
|
|
412
467
|
nextLocation: viewTransitionOpts.nextLocation
|
|
413
468
|
});
|
|
@@ -419,10 +474,10 @@ function RouterProvider(_ref) {
|
|
|
419
474
|
// When we start a view transition, create a Deferred we can use for the
|
|
420
475
|
// eventual "completed" render
|
|
421
476
|
React.useEffect(() => {
|
|
422
|
-
if (vtContext.isTransitioning) {
|
|
477
|
+
if (vtContext.isTransitioning && !vtContext.flushSync) {
|
|
423
478
|
setRenderDfd(new Deferred());
|
|
424
479
|
}
|
|
425
|
-
}, [vtContext
|
|
480
|
+
}, [vtContext]);
|
|
426
481
|
// Once the deferred is created, kick off startViewTransition() to update the
|
|
427
482
|
// DOM and then wait on the Deferred to resolve (indicating the DOM update has
|
|
428
483
|
// happened)
|
|
@@ -459,6 +514,7 @@ function RouterProvider(_ref) {
|
|
|
459
514
|
setPendingState(interruption.state);
|
|
460
515
|
setVtContext({
|
|
461
516
|
isTransitioning: true,
|
|
517
|
+
flushSync: false,
|
|
462
518
|
currentLocation: interruption.currentLocation,
|
|
463
519
|
nextLocation: interruption.nextLocation
|
|
464
520
|
});
|
|
@@ -741,7 +797,13 @@ const NavLink = /*#__PURE__*/React.forwardRef(function NavLinkWithRef(_ref8, ref
|
|
|
741
797
|
nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null;
|
|
742
798
|
toPathname = toPathname.toLowerCase();
|
|
743
799
|
}
|
|
744
|
-
|
|
800
|
+
// If the `to` has a trailing slash, look at that exact spot. Otherwise,
|
|
801
|
+
// we're looking for a slash _after_ what's in `to`. For example:
|
|
802
|
+
//
|
|
803
|
+
// <NavLink to="/users"> and <NavLink to="/users/">
|
|
804
|
+
// both want to look for a / at index 6 to match URL `/users/matt`
|
|
805
|
+
const endSlashPosition = toPathname !== "/" && toPathname.endsWith("/") ? toPathname.length - 1 : toPathname.length;
|
|
806
|
+
let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/";
|
|
745
807
|
let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/");
|
|
746
808
|
let renderProps = {
|
|
747
809
|
isActive,
|
|
@@ -972,7 +1034,8 @@ function useSubmit() {
|
|
|
972
1034
|
formData,
|
|
973
1035
|
body,
|
|
974
1036
|
formMethod: options.method || method,
|
|
975
|
-
formEncType: options.encType || encType
|
|
1037
|
+
formEncType: options.encType || encType,
|
|
1038
|
+
unstable_flushSync: options.unstable_flushSync
|
|
976
1039
|
});
|
|
977
1040
|
} else {
|
|
978
1041
|
router.navigate(options.action || action, {
|
|
@@ -984,6 +1047,7 @@ function useSubmit() {
|
|
|
984
1047
|
replace: options.replace,
|
|
985
1048
|
state: options.state,
|
|
986
1049
|
fromRouteId: currentRouteId,
|
|
1050
|
+
unstable_flushSync: options.unstable_flushSync,
|
|
987
1051
|
unstable_viewTransition: options.unstable_viewTransition
|
|
988
1052
|
});
|
|
989
1053
|
}
|
|
@@ -1006,21 +1070,19 @@ function useFormAction(action, _temp2) {
|
|
|
1006
1070
|
let path = _extends({}, useResolvedPath(action ? action : ".", {
|
|
1007
1071
|
relative
|
|
1008
1072
|
}));
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
// the intended behavior of when "." is specifically provided as
|
|
1012
|
-
// the form action, but inconsistent w/ browsers when the action is omitted.
|
|
1073
|
+
// If no action was specified, browsers will persist current search params
|
|
1074
|
+
// when determining the path, so match that behavior
|
|
1013
1075
|
// https://github.com/remix-run/remix/issues/927
|
|
1014
1076
|
let location = useLocation();
|
|
1015
1077
|
if (action == null) {
|
|
1016
1078
|
// Safe to write to this directly here since if action was undefined, we
|
|
1017
1079
|
// would have called useResolvedPath(".") which will never include a search
|
|
1018
1080
|
path.search = location.search;
|
|
1019
|
-
// When grabbing search params from the URL, remove
|
|
1020
|
-
//
|
|
1021
|
-
//
|
|
1022
|
-
|
|
1023
|
-
|
|
1081
|
+
// When grabbing search params from the URL, remove any included ?index param
|
|
1082
|
+
// since it might not apply to our contextual route. We add it back based
|
|
1083
|
+
// on match.route.index below
|
|
1084
|
+
let params = new URLSearchParams(path.search);
|
|
1085
|
+
if (params.has("index") && params.get("index") === "") {
|
|
1024
1086
|
params.delete("index");
|
|
1025
1087
|
path.search = params.toString() ? "?" + params.toString() : "";
|
|
1026
1088
|
}
|
|
@@ -1059,7 +1121,9 @@ function useFetcher(_temp3) {
|
|
|
1059
1121
|
!(routeId != null) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "useFetcher can only be used on routes that contain a unique \"id\"") : UNSAFE_invariant(false) : void 0;
|
|
1060
1122
|
// Fetcher key handling
|
|
1061
1123
|
let [fetcherKey, setFetcherKey] = React.useState(key || "");
|
|
1062
|
-
if (
|
|
1124
|
+
if (key && key !== fetcherKey) {
|
|
1125
|
+
setFetcherKey(key);
|
|
1126
|
+
} else if (!fetcherKey) {
|
|
1063
1127
|
setFetcherKey(getUniqueFetcherId());
|
|
1064
1128
|
}
|
|
1065
1129
|
// Registration/cleanup
|
|
@@ -1073,9 +1137,9 @@ function useFetcher(_temp3) {
|
|
|
1073
1137
|
};
|
|
1074
1138
|
}, [router, fetcherKey]);
|
|
1075
1139
|
// Fetcher additions
|
|
1076
|
-
let load = React.useCallback(href => {
|
|
1140
|
+
let load = React.useCallback((href, opts) => {
|
|
1077
1141
|
!routeId ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for fetcher.load()") : UNSAFE_invariant(false) : void 0;
|
|
1078
|
-
router.fetch(fetcherKey, routeId, href);
|
|
1142
|
+
router.fetch(fetcherKey, routeId, href, opts);
|
|
1079
1143
|
}, [fetcherKey, routeId, router]);
|
|
1080
1144
|
let submitImpl = useSubmit();
|
|
1081
1145
|
let submit = React.useCallback((target, opts) => {
|
|
@@ -1274,7 +1338,7 @@ function usePrompt(_ref12) {
|
|
|
1274
1338
|
when,
|
|
1275
1339
|
message
|
|
1276
1340
|
} = _ref12;
|
|
1277
|
-
let blocker =
|
|
1341
|
+
let blocker = useBlocker(when);
|
|
1278
1342
|
React.useEffect(() => {
|
|
1279
1343
|
if (blocker.state === "blocked") {
|
|
1280
1344
|
let proceed = window.confirm(message);
|