react-router-dom 6.10.0 → 6.11.0-pre.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 CHANGED
@@ -1,5 +1,36 @@
1
1
  # `react-router-dom`
2
2
 
3
+ ## 6.11.0-pre.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Add static prop to `StaticRouterProvider`'s internal `Router` component ([#10401](https://github.com/remix-run/react-router/pull/10401))
8
+ - Updated dependencies:
9
+ - `react-router@6.11.0-pre.1`
10
+
11
+ ## 6.11.0-pre.0
12
+
13
+ ### Minor Changes
14
+
15
+ - - Enable relative routing in the `@remix-run/router` when providing a source route ID from which the path is relative to: ([#10336](https://github.com/remix-run/react-router/pull/10336))
16
+
17
+ - Example: `router.navigate("../path", { fromRouteId: "some-route" })`.
18
+ - This also applies to `router.fetch` which already receives a source route ID
19
+
20
+ - Introduce a new `@remix-run/router` `future.v7_prependBasename` flag to enable `basename` prefixing to all paths coming into `router.navigate` and `router.fetch`.
21
+ - Previously the `basename` was prepended in the React Router layer, but now that relative routing is being handled by the router we need prepend the `basename` _after_ resolving any relative paths
22
+ - This also enables `basename` support in `useFetcher` as well
23
+
24
+ ### Patch Changes
25
+
26
+ - Fix inadvertent re-renders when using `Component` instead of `element` on a route definition ([#10287](https://github.com/remix-run/react-router/pull/10287))
27
+ - Fail gracefully on `<Link to="//">` and other invalid URL values ([#10367](https://github.com/remix-run/react-router/pull/10367))
28
+ - Switched from `useSyncExternalStore` to `useState` for internal `@remix-run/router` router state syncing in `<RouterProvider>`. We found some [subtle bugs](https://codesandbox.io/s/use-sync-external-store-loop-9g7b81) where router state updates got propagated _before_ other normal `useState` updates, which could lead to footguns in `useEffect` calls. ([#10377](https://github.com/remix-run/react-router/pull/10377))
29
+ - When using a `RouterProvider`, `useNavigate`/`useSubmit`/`fetcher.submit` are now stable across location changes, since we can handle relative routing via the `@remix-run/router` instance and get rid of our dependence on `useLocation()`. When using `BrowserRouter`, these hooks remain unstable across location changes because they still rely on `useLocation()`. ([#10336](https://github.com/remix-run/react-router/pull/10336))
30
+ - Updated dependencies:
31
+ - `react-router@6.11.0-pre.0`
32
+ - `@remix-run/router@1.6.0-pre.0`
33
+
3
34
  ## 6.10.0
4
35
 
5
36
  ### Minor Changes
package/LICENSE.md CHANGED
@@ -1,7 +1,8 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) React Training 2015-2019
4
- Copyright (c) Remix Software 2020-2022
3
+ Copyright (c) React Training LLC 2015-2019
4
+ Copyright (c) Remix Software Inc. 2020-2021
5
+ Copyright (c) Shopify Inc. 2022-2023
5
6
 
6
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
8
  of this software and associated documentation files (the "Software"), to deal
package/dist/dom.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { FormEncType, HTMLFormMethod } from "@remix-run/router";
2
- import type { RelativeRoutingType } from "react-router";
1
+ import type { FormEncType, HTMLFormMethod, RelativeRoutingType } from "@remix-run/router";
3
2
  export declare const defaultMethod: HTMLFormMethod;
4
3
  export declare function isHtmlElement(object: any): object is HTMLElement;
5
4
  export declare function isButtonElement(object: any): object is HTMLButtonElement;
@@ -41,9 +40,6 @@ export interface SubmitOptions {
41
40
  /**
42
41
  * The action URL path used to submit the form. Overrides `<form action>`.
43
42
  * Defaults to the path of the current route.
44
- *
45
- * Note: It is assumed the path is already resolved. If you need to resolve a
46
- * relative path, use `useFormAction`.
47
43
  */
48
44
  action?: string;
49
45
  /**
@@ -71,8 +67,8 @@ export interface SubmitOptions {
71
67
  }
72
68
  export declare function getFormSubmissionInfo(target: HTMLFormElement | HTMLButtonElement | HTMLInputElement | FormData | URLSearchParams | {
73
69
  [name: string]: string;
74
- } | null, defaultAction: string, options: SubmitOptions): {
75
- url: URL;
70
+ } | null, options: SubmitOptions, basename: string): {
71
+ action: string | null;
76
72
  method: string;
77
73
  encType: string;
78
74
  formData: FormData;
package/dist/index.d.ts CHANGED
@@ -12,13 +12,13 @@ export { createSearchParams };
12
12
  export type { ActionFunction, ActionFunctionArgs, AwaitProps, unstable_Blocker, unstable_BlockerFunction, DataRouteMatch, DataRouteObject, 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, To, } from "react-router";
13
13
  export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, RouterProvider, Routes, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, isRouteErrorResponse, generatePath, json, matchPath, matchRoutes, parsePath, redirect, renderMatches, resolvePath, useActionData, useAsyncError, useAsyncValue, unstable_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
- export { UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_NavigationContext, UNSAFE_LocationContext, UNSAFE_RouteContext, } from "react-router";
15
+ export { UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_NavigationContext, UNSAFE_LocationContext, UNSAFE_RouteContext, UNSAFE_useRouteId, } from "react-router";
16
16
  declare global {
17
17
  var __staticRouterHydrationData: HydrationState | undefined;
18
18
  }
19
19
  interface DOMRouterOpts {
20
20
  basename?: string;
21
- future?: FutureConfig;
21
+ future?: Partial<Omit<FutureConfig, "v7_prependBasename">>;
22
22
  hydrationData?: HydrationState;
23
23
  window?: Window;
24
24
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router DOM v6.10.0
2
+ * React Router DOM v6.11.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,9 +9,9 @@
9
9
  * @license MIT
10
10
  */
11
11
  import * as React from 'react';
12
- import { UNSAFE_detectErrorBoundary, Router, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, UNSAFE_DataRouterStateContext, useNavigate, createPath, UNSAFE_RouteContext, useMatches, useNavigation, unstable_useBlocker, UNSAFE_DataRouterContext } from 'react-router';
13
- export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, RouterProvider, Routes, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_LocationContext, UNSAFE_NavigationContext, UNSAFE_RouteContext, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, renderMatches, resolvePath, unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes } from 'react-router';
14
- import { createRouter, createBrowserHistory, createHashHistory, ErrorResponse, stripBasename, UNSAFE_warning, UNSAFE_invariant, joinPaths } from '@remix-run/router';
12
+ import { UNSAFE_mapRouteProperties, Router, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, UNSAFE_DataRouterStateContext, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, unstable_useBlocker, UNSAFE_DataRouterContext } from 'react-router';
13
+ export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, RouterProvider, 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, renderMatches, resolvePath, unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes } from 'react-router';
14
+ import { stripBasename, createRouter, createBrowserHistory, createHashHistory, ErrorResponse, UNSAFE_warning, UNSAFE_invariant, joinPaths } from '@remix-run/router';
15
15
 
16
16
  function _extends() {
17
17
  _extends = Object.assign ? Object.assign.bind() : function (target) {
@@ -117,16 +117,26 @@ function getSearchParamsForLocation(locationSearch, defaultSearchParams) {
117
117
 
118
118
  return searchParams;
119
119
  }
120
- function getFormSubmissionInfo(target, defaultAction, options) {
120
+ function getFormSubmissionInfo(target, options, basename) {
121
121
  let method;
122
- let action;
122
+ let action = null;
123
123
  let encType;
124
124
  let formData;
125
125
 
126
126
  if (isFormElement(target)) {
127
127
  let submissionTrigger = options.submissionTrigger;
128
+
129
+ if (options.action) {
130
+ action = options.action;
131
+ } else {
132
+ // When grabbing the action from the element, it will have had the basename
133
+ // prefixed to ensure non-JS scenarios work, so strip it since we'll
134
+ // re-prefix in the router
135
+ let attr = target.getAttribute("action");
136
+ action = attr ? stripBasename(attr, basename) : null;
137
+ }
138
+
128
139
  method = options.method || target.getAttribute("method") || defaultMethod;
129
- action = options.action || target.getAttribute("action") || defaultAction;
130
140
  encType = options.encType || target.getAttribute("enctype") || defaultEncType;
131
141
  formData = new FormData(target);
132
142
 
@@ -141,8 +151,17 @@ function getFormSubmissionInfo(target, defaultAction, options) {
141
151
  } // <button>/<input type="submit"> may override attributes of <form>
142
152
 
143
153
 
154
+ if (options.action) {
155
+ action = options.action;
156
+ } else {
157
+ // When grabbing the action from the element, it will have had the basename
158
+ // prefixed to ensure non-JS scenarios work, so strip it since we'll
159
+ // re-prefix in the router
160
+ let attr = target.getAttribute("formaction") || form.getAttribute("action");
161
+ action = attr ? stripBasename(attr, basename) : null;
162
+ }
163
+
144
164
  method = options.method || target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod;
145
- action = options.action || target.getAttribute("formaction") || form.getAttribute("action") || defaultAction;
146
165
  encType = options.encType || target.getAttribute("formenctype") || form.getAttribute("enctype") || defaultEncType;
147
166
  formData = new FormData(form); // Include name + value from a <button>, appending in case the button name
148
167
  // matches an existing input name
@@ -154,7 +173,7 @@ function getFormSubmissionInfo(target, defaultAction, options) {
154
173
  throw new Error("Cannot submit element that is not <form>, <button>, or " + "<input type=\"submit|image\">");
155
174
  } else {
156
175
  method = options.method || defaultMethod;
157
- action = options.action || defaultAction;
176
+ action = options.action || null;
158
177
  encType = options.encType || defaultEncType;
159
178
 
160
179
  if (target instanceof FormData) {
@@ -174,13 +193,8 @@ function getFormSubmissionInfo(target, defaultAction, options) {
174
193
  }
175
194
  }
176
195
 
177
- let {
178
- protocol,
179
- host
180
- } = window.location;
181
- let url = new URL(action, protocol + "//" + host);
182
196
  return {
183
- url,
197
+ action,
184
198
  method: method.toLowerCase(),
185
199
  encType,
186
200
  formData
@@ -193,25 +207,29 @@ const _excluded = ["onClick", "relative", "reloadDocument", "replace", "state",
193
207
  function createBrowserRouter(routes, opts) {
194
208
  return createRouter({
195
209
  basename: opts == null ? void 0 : opts.basename,
196
- future: opts == null ? void 0 : opts.future,
210
+ future: _extends({}, opts == null ? void 0 : opts.future, {
211
+ v7_prependBasename: true
212
+ }),
197
213
  history: createBrowserHistory({
198
214
  window: opts == null ? void 0 : opts.window
199
215
  }),
200
216
  hydrationData: (opts == null ? void 0 : opts.hydrationData) || parseHydrationData(),
201
217
  routes,
202
- detectErrorBoundary: UNSAFE_detectErrorBoundary
218
+ mapRouteProperties: UNSAFE_mapRouteProperties
203
219
  }).initialize();
204
220
  }
205
221
  function createHashRouter(routes, opts) {
206
222
  return createRouter({
207
223
  basename: opts == null ? void 0 : opts.basename,
208
- future: opts == null ? void 0 : opts.future,
224
+ future: _extends({}, opts == null ? void 0 : opts.future, {
225
+ v7_prependBasename: true
226
+ }),
209
227
  history: createHashHistory({
210
228
  window: opts == null ? void 0 : opts.window
211
229
  }),
212
230
  hydrationData: (opts == null ? void 0 : opts.hydrationData) || parseHydrationData(),
213
231
  routes,
214
- detectErrorBoundary: UNSAFE_detectErrorBoundary
232
+ mapRouteProperties: UNSAFE_mapRouteProperties
215
233
  }).initialize();
216
234
  }
217
235
 
@@ -381,15 +399,20 @@ const Link = /*#__PURE__*/React.forwardRef(function LinkWithRef(_ref4, ref) {
381
399
  absoluteHref = to; // Only check for external origins client-side
382
400
 
383
401
  if (isBrowser) {
384
- let currentUrl = new URL(window.location.href);
385
- let targetUrl = to.startsWith("//") ? new URL(currentUrl.protocol + to) : new URL(to);
386
- let path = stripBasename(targetUrl.pathname, basename);
387
-
388
- if (targetUrl.origin === currentUrl.origin && path != null) {
389
- // Strip the protocol/origin/basename for same-origin absolute URLs
390
- to = path + targetUrl.search + targetUrl.hash;
391
- } else {
392
- isExternal = true;
402
+ try {
403
+ let currentUrl = new URL(window.location.href);
404
+ let targetUrl = to.startsWith("//") ? new URL(currentUrl.protocol + to) : new URL(to);
405
+ let path = stripBasename(targetUrl.pathname, basename);
406
+
407
+ if (targetUrl.origin === currentUrl.origin && path != null) {
408
+ // Strip the protocol/origin/basename for same-origin absolute URLs
409
+ to = path + targetUrl.search + targetUrl.hash;
410
+ } else {
411
+ isExternal = true;
412
+ }
413
+ } catch (e) {
414
+ // We can't do external URL detection without a valid URL
415
+ process.env.NODE_ENV !== "production" ? UNSAFE_warning(false, "<Link to=\"" + to + "\"> contains an invalid URL which will probably break " + "when clicked - please update to a valid URL path.") : void 0;
393
416
  }
394
417
  }
395
418
  } // Rendered into <a href> for relative URLs
@@ -687,11 +710,14 @@ function useSubmit() {
687
710
  return useSubmitImpl();
688
711
  }
689
712
 
690
- function useSubmitImpl(fetcherKey, routeId) {
713
+ function useSubmitImpl(fetcherKey, fetcherRouteId) {
691
714
  let {
692
715
  router
693
716
  } = useDataRouterContext(DataRouterHook.UseSubmitImpl);
694
- let defaultAction = useFormAction();
717
+ let {
718
+ basename
719
+ } = React.useContext(UNSAFE_NavigationContext);
720
+ let currentRouteId = UNSAFE_useRouteId();
695
721
  return React.useCallback(function (target, options) {
696
722
  if (options === void 0) {
697
723
  options = {};
@@ -702,14 +728,13 @@ function useSubmitImpl(fetcherKey, routeId) {
702
728
  }
703
729
 
704
730
  let {
731
+ action,
705
732
  method,
706
733
  encType,
707
- formData,
708
- url
709
- } = getFormSubmissionInfo(target, defaultAction, options);
710
- let href = url.pathname + url.search;
734
+ formData
735
+ } = getFormSubmissionInfo(target, options, basename); // Base options shared between fetch() and navigate()
736
+
711
737
  let opts = {
712
- replace: options.replace,
713
738
  preventScrollReset: options.preventScrollReset,
714
739
  formData,
715
740
  formMethod: method,
@@ -717,13 +742,18 @@ function useSubmitImpl(fetcherKey, routeId) {
717
742
  };
718
743
 
719
744
  if (fetcherKey) {
720
- !(routeId != null) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for useFetcher()") : UNSAFE_invariant(false) : void 0;
721
- router.fetch(fetcherKey, routeId, href, opts);
745
+ !(fetcherRouteId != null) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for useFetcher()") : UNSAFE_invariant(false) : void 0;
746
+ router.fetch(fetcherKey, fetcherRouteId, action, opts);
722
747
  } else {
723
- router.navigate(href, opts);
748
+ router.navigate(action, _extends({}, opts, {
749
+ replace: options.replace,
750
+ fromRouteId: currentRouteId
751
+ }));
724
752
  }
725
- }, [defaultAction, router, fetcherKey, routeId]);
726
- }
753
+ }, [router, basename, fetcherKey, fetcherRouteId, currentRouteId]);
754
+ } // v7: Eventually we should deprecate this entirely in favor of using the
755
+ // router method directly?
756
+
727
757
 
728
758
  function useFormAction(action, _temp2) {
729
759
  let {
@@ -834,7 +864,7 @@ function useFetcher() {
834
864
  // fetcher is no longer around.
835
865
  return () => {
836
866
  if (!router) {
837
- console.warn("No fetcher available to clean up from useFetcher()");
867
+ console.warn("No router available to clean up from useFetcher()");
838
868
  return;
839
869
  }
840
870