Skip to content

Commit 5b113f7

Browse files
authored
Do not short circuit on hash change only mutation submissions (#9944)
* Do not short circuit on hash change only mutation submissions * Remove leftover debugger
1 parent 31990be commit 5b113f7

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

.changeset/orange-insects-complain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Do not short circuit on hash change only mutation submissions

packages/router/__tests__/router-test.ts

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -819,63 +819,48 @@ const TASK_ROUTES: TestRouteObject[] = [
819819
},
820820
];
821821

822-
const TM_ROUTES = [
822+
const TM_ROUTES: TestRouteObject[] = [
823823
{
824824
path: "",
825825
id: "root",
826-
827-
module: "",
828826
hasErrorBoundary: true,
829827
loader: true,
830828
children: [
831829
{
832830
path: "/",
833831
id: "index",
834-
hasLoader: true,
835832
loader: true,
836833
action: true,
837-
838-
module: "",
839834
},
840835
{
841836
path: "/foo",
842837
id: "foo",
843838
loader: true,
844839
action: true,
845-
846-
module: "",
847840
},
848841
{
849842
path: "/foo/bar",
850843
id: "foobar",
851844
loader: true,
852845
action: true,
853-
854-
module: "",
855846
},
856847
{
857848
path: "/bar",
858849
id: "bar",
859850
loader: true,
860851
action: true,
861-
862-
module: "",
863852
},
864853
{
865854
path: "/baz",
866855
id: "baz",
867856
loader: true,
868857
action: true,
869-
870-
module: "",
871858
},
872859
{
873860
path: "/p/:param",
874861
id: "param",
875862
loader: true,
876863
action: true,
877-
878-
module: "",
879864
},
880865
],
881866
},
@@ -1380,14 +1365,68 @@ describe("a router", () => {
13801365
});
13811366
});
13821367

1383-
it("does not load anything on hash change only", async () => {
1368+
it("does not load anything on hash change only <Link> navigations", async () => {
13841369
let t = initializeTmTest();
13851370
expect(t.router.state.loaderData).toMatchObject({ root: "ROOT" });
13861371
let A = await t.navigate("/#bar");
13871372
expect(A.loaders.root.stub.mock.calls.length).toBe(0);
13881373
expect(t.router.state.loaderData).toMatchObject({ root: "ROOT" });
13891374
});
13901375

1376+
it('does not load anything on hash change only empty <Form method="get"> navigations', async () => {
1377+
let t = initializeTmTest();
1378+
expect(t.router.state.loaderData).toMatchObject({ root: "ROOT" });
1379+
let A = await t.navigate("/#bar", {
1380+
formData: createFormData({}),
1381+
});
1382+
expect(A.loaders.root.stub.mock.calls.length).toBe(0);
1383+
expect(t.router.state.loaderData).toMatchObject({ root: "ROOT" });
1384+
});
1385+
1386+
it('runs loaders on hash change only non-empty <Form method="get"> navigations', async () => {
1387+
let t = initializeTmTest();
1388+
expect(t.router.state.loaderData).toMatchObject({ root: "ROOT" });
1389+
let A = await t.navigate("/#bar", {
1390+
formData: createFormData({ key: "value" }),
1391+
});
1392+
await A.loaders.root.resolve("ROOT 2");
1393+
await A.loaders.index.resolve("INDEX 2");
1394+
expect(t.router.state.location.search).toBe("?key=value");
1395+
expect(t.router.state.loaderData).toMatchObject({
1396+
root: "ROOT 2",
1397+
index: "INDEX 2",
1398+
});
1399+
});
1400+
1401+
it('runs action/loaders on hash change only <Form method="post"> navigations', async () => {
1402+
let t = initializeTmTest();
1403+
let A = await t.navigate("/foo#bar");
1404+
expect(t.router.state.navigation.state).toBe("loading");
1405+
await A.loaders.foo.resolve("A");
1406+
expect(t.router.state.loaderData).toMatchObject({
1407+
root: "ROOT",
1408+
foo: "A",
1409+
});
1410+
1411+
// Submit while we have an active hash causing us to lose it
1412+
let B = await t.navigate("/foo", {
1413+
formMethod: "post",
1414+
formData: createFormData({}),
1415+
});
1416+
expect(t.router.state.navigation.state).toBe("submitting");
1417+
await B.actions.foo.resolve("ACTION");
1418+
await B.loaders.root.resolve("ROOT 2");
1419+
await B.loaders.foo.resolve("B");
1420+
expect(t.router.state.navigation.state).toBe("idle");
1421+
expect(t.router.state.actionData).toMatchObject({
1422+
foo: "ACTION",
1423+
});
1424+
expect(t.router.state.loaderData).toMatchObject({
1425+
root: "ROOT 2",
1426+
foo: "B",
1427+
});
1428+
});
1429+
13911430
it("sets all right states on hash change only", async () => {
13921431
let t = initializeTmTest();
13931432
let key = t.router.state.location.key;
@@ -2396,7 +2435,6 @@ describe("a router", () => {
23962435
children: expect.any(Array),
23972436
id: "root",
23982437
loader: expect.any(Function),
2399-
module: "",
24002438
path: "",
24012439
},
24022440
},
@@ -2473,7 +2511,6 @@ describe("a router", () => {
24732511
children: expect.any(Array),
24742512
id: "root",
24752513
loader: expect.any(Function),
2476-
module: "",
24772514
path: "",
24782515
},
24792516
},

packages/router/router.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,8 +1121,13 @@ export function createRouter(init: RouterInit): Router {
11211121
return;
11221122
}
11231123

1124-
// Short circuit if it's only a hash change
1125-
if (isHashChangeOnly(state.location, location)) {
1124+
// Short circuit if it's only a hash change and not a mutation submission
1125+
// For example, on /page#hash and submit a <Form method="post"> which will
1126+
// default to a navigation to /page
1127+
if (
1128+
isHashChangeOnly(state.location, location) &&
1129+
!(opts && opts.submission && isMutationMethod(opts.submission.formMethod))
1130+
) {
11261131
completeNavigation(location, { matches });
11271132
return;
11281133
}

0 commit comments

Comments
 (0)