react-router-dom 6.17.0 → 6.18.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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # `react-router-dom`
2
2
 
3
+ ## 6.18.0-pre.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add support for manual fetcher key specification via `useFetcher({ key: string })` so you can access the same fetcher instance from different components in your application without prop-drilling ([RFC](https://github.com/remix-run/remix/discussions/7698)) ([#10960](https://github.com/remix-run/react-router/pull/10960))
8
+
9
+ - Fetcher keys are now also exposed on the fetchers returned from `useFetchers` so that they can be looked up by `key`
10
+
11
+ - Add `navigate`/`fetcherKey` params/props to `useSumbit`/`Form` to support kicking off a fetcher submission under the hood with an optionally user-specified `key` ([#10960](https://github.com/remix-run/react-router/pull/10960))
12
+
13
+ - Invoking a fetcher in this way is ephemeral and stateless
14
+ - If you need to access the state of one of these fetchers, you will need to leverage `useFetcher({ key })` to look it up elsewhere
15
+
16
+ ### Patch Changes
17
+
18
+ - Adds a fetcher context to `RouterProvider` that holds completed fetcher data, in preparation for the upcoming future flag that will change the fetcher persistence/cleanup behavior ([#10961](https://github.com/remix-run/react-router/pull/10961))
19
+ - Fix the `future`prop on `BrowserRouter`, `HashRouter` and `MemoryRouter` so that it accepts a `Partial<FutureConfig>` instead of requiring all flags to be included. ([#10962](https://github.com/remix-run/react-router/pull/10962))
20
+ - Updated dependencies:
21
+ - `@remix-run/router@1.11.0-pre.0`
22
+ - `react-router@6.18.0-pre.0`
23
+
3
24
  ## 6.17.0
4
25
 
5
26
  ### Minor Changes
package/dist/dom.d.ts CHANGED
@@ -56,6 +56,14 @@ export interface SubmitOptions {
56
56
  * Defaults to "application/x-www-form-urlencoded".
57
57
  */
58
58
  encType?: FormEncType;
59
+ /**
60
+ * Indicate a specific fetcherKey to use when using navigate=false
61
+ */
62
+ fetcherKey?: string;
63
+ /**
64
+ * navigate=false will use a fetcher instead of a navigation
65
+ */
66
+ navigate?: boolean;
59
67
  /**
60
68
  * Set `true` to replace the current entry in the browser's history stack
61
69
  * instead of creating a new one (i.e. stay on "the same page"). Defaults
package/dist/index.d.ts CHANGED
@@ -36,6 +36,13 @@ type ViewTransitionContextObject = {
36
36
  };
37
37
  declare const ViewTransitionContext: React.Context<ViewTransitionContextObject>;
38
38
  export { ViewTransitionContext as UNSAFE_ViewTransitionContext };
39
+ type FetchersContextObject = {
40
+ fetcherData: Map<string, any>;
41
+ register: (key: string) => void;
42
+ unregister: (key: string) => void;
43
+ };
44
+ declare const FetchersContext: React.Context<FetchersContextObject | null>;
45
+ export { FetchersContext as UNSAFE_FetchersContext };
39
46
  interface ViewTransition {
40
47
  finished: Promise<void>;
41
48
  ready: Promise<void>;
@@ -49,7 +56,7 @@ export declare function RouterProvider({ fallbackElement, router, future, }: Rou
49
56
  export interface BrowserRouterProps {
50
57
  basename?: string;
51
58
  children?: React.ReactNode;
52
- future?: FutureConfig;
59
+ future?: Partial<FutureConfig>;
53
60
  window?: Window;
54
61
  }
55
62
  /**
@@ -59,7 +66,7 @@ export declare function BrowserRouter({ basename, children, future, window, }: B
59
66
  export interface HashRouterProps {
60
67
  basename?: string;
61
68
  children?: React.ReactNode;
62
- future?: FutureConfig;
69
+ future?: Partial<FutureConfig>;
63
70
  window?: Window;
64
71
  }
65
72
  /**
@@ -147,6 +154,14 @@ export interface FetcherFormProps extends React.FormHTMLAttributes<HTMLFormEleme
147
154
  onSubmit?: React.FormEventHandler<HTMLFormElement>;
148
155
  }
149
156
  export interface FormProps extends FetcherFormProps {
157
+ /**
158
+ * Indicate a specific fetcherKey to use when using navigate=false
159
+ */
160
+ fetcherKey?: string;
161
+ /**
162
+ * navigate=false will use a fetcher instead of a navigation
163
+ */
164
+ navigate?: boolean;
150
165
  /**
151
166
  * Forces a full document navigation instead of a fetch.
152
167
  */
@@ -238,9 +253,8 @@ export declare function useSubmit(): SubmitFunction;
238
253
  export declare function useFormAction(action?: string, { relative }?: {
239
254
  relative?: RelativeRoutingType;
240
255
  }): string;
241
- declare function createFetcherForm(fetcherKey: string, routeId: string): React.ForwardRefExoticComponent<FetcherFormProps & React.RefAttributes<HTMLFormElement>>;
242
256
  export type FetcherWithComponents<TData> = Fetcher<TData> & {
243
- Form: ReturnType<typeof createFetcherForm>;
257
+ Form: React.ForwardRefExoticComponent<FetcherFormProps & React.RefAttributes<HTMLFormElement>>;
244
258
  submit: FetcherSubmitFunction;
245
259
  load: (href: string) => void;
246
260
  };
@@ -248,12 +262,16 @@ export type FetcherWithComponents<TData> = Fetcher<TData> & {
248
262
  * Interacts with route loaders and actions without causing a navigation. Great
249
263
  * for any interaction that stays on the same page.
250
264
  */
251
- export declare function useFetcher<TData = any>(): FetcherWithComponents<TData>;
265
+ export declare function useFetcher<TData = any>({ key, }?: {
266
+ key?: string;
267
+ }): FetcherWithComponents<TData>;
252
268
  /**
253
269
  * Provides all fetchers currently on the page. Useful for layouts and parent
254
270
  * routes that need to provide pending/optimistic UI regarding the fetch.
255
271
  */
256
- export declare function useFetchers(): Fetcher[];
272
+ export declare function useFetchers(): (Fetcher & {
273
+ key: string;
274
+ })[];
257
275
  /**
258
276
  * When rendered inside a RouterProvider, will restore scroll positions on navigations
259
277
  */
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router DOM v6.17.0
2
+ * React Router DOM v6.18.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -11,7 +11,7 @@
11
11
  import * as React from 'react';
12
12
  import { UNSAFE_mapRouteProperties, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, Router, UNSAFE_useRoutesImpl, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, unstable_useBlocker } from 'react-router';
13
13
  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, 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, UNSAFE_warning, createRouter, createBrowserHistory, createHashHistory, UNSAFE_ErrorResponseImpl, UNSAFE_invariant, joinPaths, matchPath } from '@remix-run/router';
14
+ import { stripBasename, UNSAFE_warning, createRouter, createBrowserHistory, createHashHistory, UNSAFE_ErrorResponseImpl, UNSAFE_invariant, joinPaths, IDLE_FETCHER, matchPath } from '@remix-run/router';
15
15
 
16
16
  function _extends() {
17
17
  _extends = Object.assign ? Object.assign.bind() : function (target) {
@@ -208,7 +208,7 @@ function getFormSubmissionInfo(target, basename) {
208
208
 
209
209
  const _excluded = ["onClick", "relative", "reloadDocument", "replace", "state", "target", "to", "preventScrollReset", "unstable_viewTransition"],
210
210
  _excluded2 = ["aria-current", "caseSensitive", "className", "end", "style", "to", "unstable_viewTransition", "children"],
211
- _excluded3 = ["reloadDocument", "replace", "state", "method", "action", "onSubmit", "submit", "relative", "preventScrollReset", "unstable_viewTransition"];
211
+ _excluded3 = ["fetcherKey", "navigate", "reloadDocument", "replace", "state", "method", "action", "onSubmit", "relative", "preventScrollReset", "unstable_viewTransition"];
212
212
  function createBrowserRouter(routes, opts) {
213
213
  return createRouter({
214
214
  basename: opts == null ? void 0 : opts.basename,
@@ -294,6 +294,10 @@ const ViewTransitionContext = /*#__PURE__*/React.createContext({
294
294
  if (process.env.NODE_ENV !== "production") {
295
295
  ViewTransitionContext.displayName = "ViewTransition";
296
296
  }
297
+ const FetchersContext = /*#__PURE__*/React.createContext(null);
298
+ if (process.env.NODE_ENV !== "production") {
299
+ FetchersContext.displayName = "Fetchers";
300
+ }
297
301
  //#endregion
298
302
  ////////////////////////////////////////////////////////////////////////////////
299
303
  //#region Components
@@ -356,6 +360,10 @@ function RouterProvider(_ref) {
356
360
  router,
357
361
  future
358
362
  } = _ref;
363
+ let {
364
+ fetcherContext,
365
+ fetcherData
366
+ } = useFetcherDataLayer();
359
367
  let [state, setStateImpl] = React.useState(router.state);
360
368
  let [pendingState, setPendingState] = React.useState();
361
369
  let [vtContext, setVtContext] = React.useState({
@@ -378,6 +386,11 @@ function RouterProvider(_ref) {
378
386
  let {
379
387
  unstable_viewTransitionOpts: viewTransitionOpts
380
388
  } = _ref2;
389
+ newState.fetchers.forEach((fetcher, key) => {
390
+ if (fetcher.data !== undefined) {
391
+ fetcherData.current.set(key, fetcher.data);
392
+ }
393
+ });
381
394
  if (!viewTransitionOpts || router.window == null || typeof router.window.document.startViewTransition !== "function") {
382
395
  // Mid-navigation state update, or startViewTransition isn't available
383
396
  optInStartTransition(() => setStateImpl(newState));
@@ -400,7 +413,7 @@ function RouterProvider(_ref) {
400
413
  nextLocation: viewTransitionOpts.nextLocation
401
414
  });
402
415
  }
403
- }, [optInStartTransition, transition, renderDfd, router.window]);
416
+ }, [router.window, transition, renderDfd, fetcherData, optInStartTransition]);
404
417
  // Need to use a layout effect here so we are subscribed early enough to
405
418
  // pick up on any render-driven redirects/navigations (useEffect/<Navigate>)
406
419
  React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);
@@ -486,6 +499,8 @@ function RouterProvider(_ref) {
486
499
  value: dataRouterContext
487
500
  }, /*#__PURE__*/React.createElement(UNSAFE_DataRouterStateContext.Provider, {
488
501
  value: state
502
+ }, /*#__PURE__*/React.createElement(FetchersContext.Provider, {
503
+ value: fetcherContext
489
504
  }, /*#__PURE__*/React.createElement(ViewTransitionContext.Provider, {
490
505
  value: vtContext
491
506
  }, /*#__PURE__*/React.createElement(Router, {
@@ -496,7 +511,7 @@ function RouterProvider(_ref) {
496
511
  }, state.initialized ? /*#__PURE__*/React.createElement(DataRoutes, {
497
512
  routes: router.routes,
498
513
  state: state
499
- }) : fallbackElement)))), null);
514
+ }) : fallbackElement))))), null);
500
515
  }
501
516
  function DataRoutes(_ref3) {
502
517
  let {
@@ -765,34 +780,26 @@ if (process.env.NODE_ENV !== "production") {
765
780
  * requests, allowing components to add nicer UX to the page as the form is
766
781
  * submitted and returns with data.
767
782
  */
768
- const Form = /*#__PURE__*/React.forwardRef((props, ref) => {
769
- let submit = useSubmit();
770
- return /*#__PURE__*/React.createElement(FormImpl, _extends({}, props, {
771
- submit: submit,
772
- ref: ref
773
- }));
774
- });
775
- if (process.env.NODE_ENV !== "production") {
776
- Form.displayName = "Form";
777
- }
778
- const FormImpl = /*#__PURE__*/React.forwardRef((_ref9, forwardedRef) => {
783
+ const Form = /*#__PURE__*/React.forwardRef((_ref9, forwardedRef) => {
779
784
  let {
785
+ fetcherKey,
786
+ navigate,
780
787
  reloadDocument,
781
788
  replace,
782
789
  state,
783
790
  method = defaultMethod,
784
791
  action,
785
792
  onSubmit,
786
- submit,
787
793
  relative,
788
794
  preventScrollReset,
789
795
  unstable_viewTransition
790
796
  } = _ref9,
791
797
  props = _objectWithoutPropertiesLoose(_ref9, _excluded3);
792
- let formMethod = method.toLowerCase() === "get" ? "get" : "post";
798
+ let submit = useSubmit();
793
799
  let formAction = useFormAction(action, {
794
800
  relative
795
801
  });
802
+ let formMethod = method.toLowerCase() === "get" ? "get" : "post";
796
803
  let submitHandler = event => {
797
804
  onSubmit && onSubmit(event);
798
805
  if (event.defaultPrevented) return;
@@ -800,7 +807,9 @@ const FormImpl = /*#__PURE__*/React.forwardRef((_ref9, forwardedRef) => {
800
807
  let submitter = event.nativeEvent.submitter;
801
808
  let submitMethod = (submitter == null ? void 0 : submitter.getAttribute("formmethod")) || method;
802
809
  submit(submitter || event.currentTarget, {
810
+ fetcherKey,
803
811
  method: submitMethod,
812
+ navigate,
804
813
  replace,
805
814
  state,
806
815
  relative,
@@ -816,7 +825,7 @@ const FormImpl = /*#__PURE__*/React.forwardRef((_ref9, forwardedRef) => {
816
825
  }, props));
817
826
  });
818
827
  if (process.env.NODE_ENV !== "production") {
819
- FormImpl.displayName = "FormImpl";
828
+ Form.displayName = "Form";
820
829
  }
821
830
  /**
822
831
  * This component will emulate the browser's scroll restoration on location
@@ -850,9 +859,11 @@ var DataRouterHook;
850
859
  })(DataRouterHook || (DataRouterHook = {}));
851
860
  var DataRouterStateHook;
852
861
  (function (DataRouterStateHook) {
862
+ DataRouterStateHook["UseFetcher"] = "useFetcher";
853
863
  DataRouterStateHook["UseFetchers"] = "useFetchers";
854
864
  DataRouterStateHook["UseScrollRestoration"] = "useScrollRestoration";
855
865
  })(DataRouterStateHook || (DataRouterStateHook = {}));
866
+ // Internal hooks
856
867
  function getDataRouterConsoleError(hookName) {
857
868
  return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
858
869
  }
@@ -866,6 +877,37 @@ function useDataRouterState(hookName) {
866
877
  !state ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : UNSAFE_invariant(false) : void 0;
867
878
  return state;
868
879
  }
880
+ function useFetcherDataLayer() {
881
+ let fetcherRefs = React.useRef(new Map());
882
+ let fetcherData = React.useRef(new Map());
883
+ let registerFetcher = React.useCallback(key => {
884
+ let count = fetcherRefs.current.get(key);
885
+ if (count == null) {
886
+ fetcherRefs.current.set(key, 1);
887
+ } else {
888
+ fetcherRefs.current.set(key, count + 1);
889
+ }
890
+ }, [fetcherRefs]);
891
+ let unregisterFetcher = React.useCallback(key => {
892
+ let count = fetcherRefs.current.get(key);
893
+ if (count == null || count <= 1) {
894
+ fetcherRefs.current.delete(key);
895
+ fetcherData.current.delete(key);
896
+ } else {
897
+ fetcherRefs.current.set(key, count - 1);
898
+ }
899
+ }, [fetcherData, fetcherRefs]);
900
+ let fetcherContext = React.useMemo(() => ({
901
+ fetcherData: fetcherData.current,
902
+ register: registerFetcher,
903
+ unregister: unregisterFetcher
904
+ }), [fetcherData, registerFetcher, unregisterFetcher]);
905
+ return {
906
+ fetcherContext,
907
+ fetcherData
908
+ };
909
+ }
910
+ // External hooks
869
911
  /**
870
912
  * Handles the click behavior for router `<Link>` components. This is useful if
871
913
  * you need to create custom `<Link>` components with the same click behavior we
@@ -928,6 +970,8 @@ function validateClientSideSubmission() {
928
970
  throw new Error("You are calling submit during the server render. " + "Try calling submit within a `useEffect` or callback instead.");
929
971
  }
930
972
  }
973
+ let fetcherId = 0;
974
+ let getUniqueFetcherId = () => "__" + String(++fetcherId) + "__";
931
975
  /**
932
976
  * Returns a function that may be used to programmatically submit a form (or
933
977
  * some arbitrary data) to the server.
@@ -952,50 +996,29 @@ function useSubmit() {
952
996
  formData,
953
997
  body
954
998
  } = getFormSubmissionInfo(target, basename);
955
- router.navigate(options.action || action, {
956
- preventScrollReset: options.preventScrollReset,
957
- formData,
958
- body,
959
- formMethod: options.method || method,
960
- formEncType: options.encType || encType,
961
- replace: options.replace,
962
- state: options.state,
963
- fromRouteId: currentRouteId,
964
- unstable_viewTransition: options.unstable_viewTransition
965
- });
966
- }, [router, basename, currentRouteId]);
967
- }
968
- /**
969
- * Returns the implementation for fetcher.submit
970
- */
971
- function useSubmitFetcher(fetcherKey, fetcherRouteId) {
972
- let {
973
- router
974
- } = useDataRouterContext(DataRouterHook.UseSubmitFetcher);
975
- let {
976
- basename
977
- } = React.useContext(UNSAFE_NavigationContext);
978
- return React.useCallback(function (target, options) {
979
- if (options === void 0) {
980
- options = {};
999
+ if (options.navigate === false) {
1000
+ let key = options.fetcherKey || getUniqueFetcherId();
1001
+ router.fetch(key, currentRouteId, options.action || action, {
1002
+ preventScrollReset: options.preventScrollReset,
1003
+ formData,
1004
+ body,
1005
+ formMethod: options.method || method,
1006
+ formEncType: options.encType || encType
1007
+ });
1008
+ } else {
1009
+ router.navigate(options.action || action, {
1010
+ preventScrollReset: options.preventScrollReset,
1011
+ formData,
1012
+ body,
1013
+ formMethod: options.method || method,
1014
+ formEncType: options.encType || encType,
1015
+ replace: options.replace,
1016
+ state: options.state,
1017
+ fromRouteId: currentRouteId,
1018
+ unstable_viewTransition: options.unstable_viewTransition
1019
+ });
981
1020
  }
982
- validateClientSideSubmission();
983
- let {
984
- action,
985
- method,
986
- encType,
987
- formData,
988
- body
989
- } = getFormSubmissionInfo(target, basename);
990
- !(fetcherRouteId != null) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for useFetcher()") : UNSAFE_invariant(false) : void 0;
991
- router.fetch(fetcherKey, fetcherRouteId, options.action || action, {
992
- preventScrollReset: options.preventScrollReset,
993
- formData,
994
- body,
995
- formMethod: options.method || method,
996
- formEncType: options.encType || encType
997
- });
998
- }, [router, basename, fetcherKey, fetcherRouteId]);
1021
+ }, [router, basename, currentRouteId]);
999
1022
  }
1000
1023
  // v7: Eventually we should deprecate this entirely in favor of using the
1001
1024
  // router method directly?
@@ -1045,63 +1068,83 @@ function useFormAction(action, _temp2) {
1045
1068
  }
1046
1069
  return createPath(path);
1047
1070
  }
1048
- function createFetcherForm(fetcherKey, routeId) {
1049
- let FetcherForm = /*#__PURE__*/React.forwardRef((props, ref) => {
1050
- let submit = useSubmitFetcher(fetcherKey, routeId);
1051
- return /*#__PURE__*/React.createElement(FormImpl, _extends({}, props, {
1052
- ref: ref,
1053
- submit: submit
1054
- }));
1055
- });
1056
- if (process.env.NODE_ENV !== "production") {
1057
- FetcherForm.displayName = "fetcher.Form";
1058
- }
1059
- return FetcherForm;
1060
- }
1061
- let fetcherId = 0;
1062
1071
  // TODO: (v7) Change the useFetcher generic default from `any` to `unknown`
1063
1072
  /**
1064
1073
  * Interacts with route loaders and actions without causing a navigation. Great
1065
1074
  * for any interaction that stays on the same page.
1066
1075
  */
1067
- function useFetcher() {
1076
+ function useFetcher(_temp3) {
1068
1077
  var _route$matches;
1078
+ let {
1079
+ key
1080
+ } = _temp3 === void 0 ? {} : _temp3;
1069
1081
  let {
1070
1082
  router
1071
1083
  } = useDataRouterContext(DataRouterHook.UseFetcher);
1084
+ let state = useDataRouterState(DataRouterStateHook.UseFetcher);
1085
+ let fetchersContext = React.useContext(FetchersContext);
1072
1086
  let route = React.useContext(UNSAFE_RouteContext);
1073
- !route ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "useFetcher must be used inside a RouteContext") : UNSAFE_invariant(false) : void 0;
1074
1087
  let routeId = (_route$matches = route.matches[route.matches.length - 1]) == null ? void 0 : _route$matches.route.id;
1088
+ !fetchersContext ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "useFetcher must be used inside a FetchersContext") : UNSAFE_invariant(false) : void 0;
1089
+ !route ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "useFetcher must be used inside a RouteContext") : UNSAFE_invariant(false) : void 0;
1075
1090
  !(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;
1076
- let [fetcherKey] = React.useState(() => String(++fetcherId));
1077
- let [Form] = React.useState(() => {
1078
- !routeId ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for fetcher.Form()") : UNSAFE_invariant(false) : void 0;
1079
- return createFetcherForm(fetcherKey, routeId);
1080
- });
1081
- let [load] = React.useState(() => href => {
1082
- !router ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No router available for fetcher.load()") : UNSAFE_invariant(false) : void 0;
1091
+ // Fetcher key handling
1092
+ let [fetcherKey, setFetcherKey] = React.useState(key || "");
1093
+ if (!fetcherKey) {
1094
+ setFetcherKey(getUniqueFetcherId());
1095
+ }
1096
+ // Registration/cleanup
1097
+ let {
1098
+ fetcherData,
1099
+ register,
1100
+ unregister
1101
+ } = fetchersContext;
1102
+ React.useEffect(() => {
1103
+ register(fetcherKey);
1104
+ return () => {
1105
+ // Unregister from ref counting for the data layer
1106
+ unregister(fetcherKey);
1107
+ // Tell the router we've unmounted - if v7_fetcherPersist is enabled this
1108
+ // will not delete immediately but instead queue up a delete after the
1109
+ // fetcher returns to an `idle` state
1110
+ router.deleteFetcher(fetcherKey);
1111
+ };
1112
+ }, [router, fetcherKey, register, unregister]);
1113
+ // Fetcher additions
1114
+ let load = React.useCallback(href => {
1083
1115
  !routeId ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for fetcher.load()") : UNSAFE_invariant(false) : void 0;
1084
1116
  router.fetch(fetcherKey, routeId, href);
1085
- });
1086
- let submit = useSubmitFetcher(fetcherKey, routeId);
1087
- let fetcher = router.getFetcher(fetcherKey);
1117
+ }, [fetcherKey, routeId, router]);
1118
+ let submitImpl = useSubmit();
1119
+ let submit = React.useCallback((target, opts) => {
1120
+ submitImpl(target, _extends({}, opts, {
1121
+ navigate: false,
1122
+ fetcherKey
1123
+ }));
1124
+ }, [fetcherKey, submitImpl]);
1125
+ let FetcherForm = React.useMemo(() => {
1126
+ let FetcherForm = /*#__PURE__*/React.forwardRef((props, ref) => {
1127
+ return /*#__PURE__*/React.createElement(Form, _extends({}, props, {
1128
+ navigate: false,
1129
+ fetcherKey: fetcherKey,
1130
+ ref: ref
1131
+ }));
1132
+ });
1133
+ if (process.env.NODE_ENV !== "production") {
1134
+ FetcherForm.displayName = "fetcher.Form";
1135
+ }
1136
+ return FetcherForm;
1137
+ }, [fetcherKey]);
1138
+ // Exposed FetcherWithComponents
1139
+ let fetcher = state.fetchers.get(fetcherKey) || IDLE_FETCHER;
1140
+ let data = fetcherData.get(fetcherKey);
1088
1141
  let fetcherWithComponents = React.useMemo(() => _extends({
1089
- Form,
1142
+ Form: FetcherForm,
1090
1143
  submit,
1091
1144
  load
1092
- }, fetcher), [fetcher, Form, submit, load]);
1093
- React.useEffect(() => {
1094
- // Is this busted when the React team gets real weird and calls effects
1095
- // twice on mount? We really just need to garbage collect here when this
1096
- // fetcher is no longer around.
1097
- return () => {
1098
- if (!router) {
1099
- console.warn("No router available to clean up from useFetcher()");
1100
- return;
1101
- }
1102
- router.deleteFetcher(fetcherKey);
1103
- };
1104
- }, [router, fetcherKey]);
1145
+ }, fetcher, {
1146
+ data
1147
+ }), [FetcherForm, submit, load, fetcher, data]);
1105
1148
  return fetcherWithComponents;
1106
1149
  }
1107
1150
  /**
@@ -1110,18 +1153,23 @@ function useFetcher() {
1110
1153
  */
1111
1154
  function useFetchers() {
1112
1155
  let state = useDataRouterState(DataRouterStateHook.UseFetchers);
1113
- return [...state.fetchers.values()];
1156
+ return Array.from(state.fetchers.entries()).map(_ref11 => {
1157
+ let [key, fetcher] = _ref11;
1158
+ return _extends({}, fetcher, {
1159
+ key
1160
+ });
1161
+ });
1114
1162
  }
1115
1163
  const SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions";
1116
1164
  let savedScrollPositions = {};
1117
1165
  /**
1118
1166
  * When rendered inside a RouterProvider, will restore scroll positions on navigations
1119
1167
  */
1120
- function useScrollRestoration(_temp3) {
1168
+ function useScrollRestoration(_temp4) {
1121
1169
  let {
1122
1170
  getKey,
1123
1171
  storageKey
1124
- } = _temp3 === void 0 ? {} : _temp3;
1172
+ } = _temp4 === void 0 ? {} : _temp4;
1125
1173
  let {
1126
1174
  router
1127
1175
  } = useDataRouterContext(DataRouterHook.UseScrollRestoration);
@@ -1259,11 +1307,11 @@ function usePageHide(callback, options) {
1259
1307
  * very incorrectly in some cases) across browsers if user click addition
1260
1308
  * back/forward navigations while the confirm is open. Use at your own risk.
1261
1309
  */
1262
- function usePrompt(_ref11) {
1310
+ function usePrompt(_ref12) {
1263
1311
  let {
1264
1312
  when,
1265
1313
  message
1266
- } = _ref11;
1314
+ } = _ref12;
1267
1315
  let blocker = unstable_useBlocker(when);
1268
1316
  React.useEffect(() => {
1269
1317
  if (blocker.state === "blocked") {
@@ -1326,5 +1374,5 @@ function useViewTransitionState(to, opts) {
1326
1374
  }
1327
1375
  //#endregion
1328
1376
 
1329
- export { BrowserRouter, Form, HashRouter, Link, NavLink, RouterProvider, ScrollRestoration, ViewTransitionContext as UNSAFE_ViewTransitionContext, useScrollRestoration as UNSAFE_useScrollRestoration, createBrowserRouter, createHashRouter, createSearchParams, HistoryRouter as unstable_HistoryRouter, usePrompt as unstable_usePrompt, useViewTransitionState as unstable_useViewTransitionState, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, useSearchParams, useSubmit };
1377
+ export { BrowserRouter, Form, HashRouter, Link, NavLink, RouterProvider, ScrollRestoration, FetchersContext as UNSAFE_FetchersContext, ViewTransitionContext as UNSAFE_ViewTransitionContext, useScrollRestoration as UNSAFE_useScrollRestoration, createBrowserRouter, createHashRouter, createSearchParams, HistoryRouter as unstable_HistoryRouter, usePrompt as unstable_usePrompt, useViewTransitionState as unstable_useViewTransitionState, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, useSearchParams, useSubmit };
1330
1378
  //# sourceMappingURL=index.js.map