diff --git a/packages/router/__tests__/router-test.ts b/packages/router/__tests__/router-test.ts index 22b0c8c16a..06d5cc4c1f 100644 --- a/packages/router/__tests__/router-test.ts +++ b/packages/router/__tests__/router-test.ts @@ -8702,8 +8702,18 @@ describe("a router", () => { expect(A.fetcher.state).toBe("submitting"); let AR = await A.actions.foo.redirect("/bar"); expect(A.fetcher.state).toBe("loading"); - expect(t.router.state.navigation.state).toBe("loading"); - expect(t.router.state.navigation.location?.pathname).toBe("/bar"); + expect(t.router.state.navigation).toMatchObject({ + state: "loading", + location: { + pathname: "/bar", + }, + // Fetcher action redirect should not proxy the fetcher submission + // onto the loading navigation + formAction: undefined, + formData: undefined, + formEncType: undefined, + formMethod: undefined, + }); await AR.loaders.root.resolve("ROOT*"); await AR.loaders.bar.resolve("stuff"); expect(A.fetcher).toEqual({ diff --git a/packages/router/router.ts b/packages/router/router.ts index 5c81212149..575b8e2a85 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -1152,6 +1152,7 @@ export function createRouter(init: RouterInit): Router { location: Location, opts?: { submission?: Submission; + fetcherSubmission?: Submission; overrideNavigation?: Navigation; pendingError?: ErrorResponse; startUninterruptedRevalidation?: boolean; @@ -1263,6 +1264,7 @@ export function createRouter(init: RouterInit): Router { matches, loadingNavigation, opts && opts.submission, + opts && opts.fetcherSubmission, opts && opts.replace, pendingActionData, pendingError @@ -1385,6 +1387,7 @@ export function createRouter(init: RouterInit): Router { matches: AgnosticDataRouteMatch[], overrideNavigation?: Navigation, submission?: Submission, + fetcherSubmission?: Submission, replace?: boolean, pendingActionData?: RouteData, pendingError?: RouteData @@ -1406,19 +1409,20 @@ export function createRouter(init: RouterInit): Router { // If this was a redirect from an action we don't have a "submission" but // we have it on the loading navigation so use that if available - let activeSubmission = submission - ? submission - : loadingNavigation.formMethod && - loadingNavigation.formAction && - loadingNavigation.formData && - loadingNavigation.formEncType - ? { - formMethod: loadingNavigation.formMethod, - formAction: loadingNavigation.formAction, - formData: loadingNavigation.formData, - formEncType: loadingNavigation.formEncType, - } - : undefined; + let activeSubmission = + submission || fetcherSubmission + ? submission || fetcherSubmission + : loadingNavigation.formMethod && + loadingNavigation.formAction && + loadingNavigation.formData && + loadingNavigation.formEncType + ? { + formMethod: loadingNavigation.formMethod, + formAction: loadingNavigation.formAction, + formData: loadingNavigation.formData, + formEncType: loadingNavigation.formEncType, + } + : undefined; let routesToUse = inFlightDataRoutes || dataRoutes; let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad( @@ -2053,6 +2057,22 @@ export function createRouter(init: RouterInit): Router { // Preserve this flag across redirects preventScrollReset: pendingPreventScrollReset, }); + } else if (isFetchActionRedirect) { + // For a fetch action redirect, we kick off a new loading navigation + // without the fetcher submission, but we send it along for shouldRevalidate + await startNavigation(redirectHistoryAction, redirectLocation, { + overrideNavigation: { + state: "loading", + location: redirectLocation, + formMethod: undefined, + formAction: undefined, + formEncType: undefined, + formData: undefined, + }, + fetcherSubmission: submission, + // Preserve this flag across redirects + preventScrollReset: pendingPreventScrollReset, + }); } else { // Otherwise, we kick off a new loading navigation, preserving the // submission info for the duration of this navigation