Skip to content

Commit be8a259

Browse files
authored
Fix fetcher.submit types (#11631)
1 parent 3a27b1f commit be8a259

File tree

4 files changed

+64
-31
lines changed

4 files changed

+64
-31
lines changed

.changeset/gorgeous-geese-sit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router-dom": patch
3+
---
4+
5+
Fix `fetcher.submit` types - remove incorrect `navigate`/`fetcherKey`/`unstable_viewTransition` options because they are only relevant for `useSubmit`

packages/react-router-dom/dom.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ function isFormDataSubmitterSupported() {
150150
return _formDataSupportsSubmitter;
151151
}
152152

153-
export interface SubmitOptions {
153+
/**
154+
* Submit options shared by both navigations and fetchers
155+
*/
156+
interface SharedSubmitOptions {
154157
/**
155158
* The HTTP method used to submit the form. Overrides `<form method>`.
156159
* Defaults to "GET".
@@ -170,15 +173,33 @@ export interface SubmitOptions {
170173
encType?: FormEncType;
171174

172175
/**
173-
* Indicate a specific fetcherKey to use when using navigate=false
176+
* Determines whether the form action is relative to the route hierarchy or
177+
* the pathname. Use this if you want to opt out of navigating the route
178+
* hierarchy and want to instead route based on /-delimited URL segments
174179
*/
175-
fetcherKey?: string;
180+
relative?: RelativeRoutingType;
176181

177182
/**
178-
* navigate=false will use a fetcher instead of a navigation
183+
* In browser-based environments, prevent resetting scroll after this
184+
* navigation when using the <ScrollRestoration> component
179185
*/
180-
navigate?: boolean;
186+
preventScrollReset?: boolean;
187+
188+
/**
189+
* Enable flushSync for this submission's state updates
190+
*/
191+
unstable_flushSync?: boolean;
192+
}
193+
194+
/**
195+
* Submit options available to fetchers
196+
*/
197+
export interface FetcherSubmitOptions extends SharedSubmitOptions {}
181198

199+
/**
200+
* Submit options available to navigations
201+
*/
202+
export interface SubmitOptions extends FetcherSubmitOptions {
182203
/**
183204
* Set `true` to replace the current entry in the browser's history stack
184205
* instead of creating a new one (i.e. stay on "the same page"). Defaults
@@ -192,22 +213,14 @@ export interface SubmitOptions {
192213
state?: any;
193214

194215
/**
195-
* Determines whether the form action is relative to the route hierarchy or
196-
* the pathname. Use this if you want to opt out of navigating the route
197-
* hierarchy and want to instead route based on /-delimited URL segments
198-
*/
199-
relative?: RelativeRoutingType;
200-
201-
/**
202-
* In browser-based environments, prevent resetting scroll after this
203-
* navigation when using the <ScrollRestoration> component
216+
* Indicate a specific fetcherKey to use when using navigate=false
204217
*/
205-
preventScrollReset?: boolean;
218+
fetcherKey?: string;
206219

207220
/**
208-
* Enable flushSync for this navigation's state updates
221+
* navigate=false will use a fetcher instead of a navigation
209222
*/
210-
unstable_flushSync?: boolean;
223+
navigate?: boolean;
211224

212225
/**
213226
* Enable view transitions on this submission navigation

packages/react-router-dom/index.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import type {
7272
ParamKeyValuePair,
7373
URLSearchParamsInit,
7474
SubmitTarget,
75+
FetcherSubmitOptions,
7576
} from "./dom";
7677
import {
7778
createSearchParams,
@@ -1158,8 +1159,10 @@ if (__DEV__) {
11581159
NavLink.displayName = "NavLink";
11591160
}
11601161

1161-
export interface FetcherFormProps
1162-
extends React.FormHTMLAttributes<HTMLFormElement> {
1162+
/**
1163+
* Form props shared by navigations and fetchers
1164+
*/
1165+
interface SharedFormProps extends React.FormHTMLAttributes<HTMLFormElement> {
11631166
/**
11641167
* The HTTP verb to use when the form is submit. Supports "get", "post",
11651168
* "put", "delete", "patch".
@@ -1200,7 +1203,15 @@ export interface FetcherFormProps
12001203
onSubmit?: React.FormEventHandler<HTMLFormElement>;
12011204
}
12021205

1203-
export interface FormProps extends FetcherFormProps {
1206+
/**
1207+
* Form props available to fetchers
1208+
*/
1209+
export interface FetcherFormProps extends SharedFormProps {}
1210+
1211+
/**
1212+
* Form props available to navigations
1213+
*/
1214+
export interface FormProps extends SharedFormProps {
12041215
/**
12051216
* Indicate a specific fetcherKey to use when using navigate=false
12061217
*/
@@ -1523,7 +1534,7 @@ export interface FetcherSubmitFunction {
15231534
(
15241535
target: SubmitTarget,
15251536
// Fetchers cannot replace or set state because they are not navigation events
1526-
options?: Omit<SubmitOptions, "replace" | "state">
1537+
options?: FetcherSubmitOptions
15271538
): void;
15281539
}
15291540

packages/router/__tests__/navigation-test.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,13 @@ describe("navigations", () => {
169169
})
170170
);
171171
expect(t.router.state.loaderData).toEqual({});
172-
expect(t.router.state.errors).toMatchInlineSnapshot(`
173-
{
174-
"foo": [SyntaxError: Unexpected token } in JSON at position 15],
175-
}
176-
`);
172+
173+
// Node 16/18 versus 20 output different errors here :/
174+
let expected =
175+
process.version.startsWith("v16") || process.version.startsWith("v18")
176+
? "Unexpected token } in JSON at position 15"
177+
: "Unexpected non-whitespace character after JSON at position 15";
178+
expect(t.router.state.errors?.foo).toEqual(new SyntaxError(expected));
177179
});
178180

179181
it("bubbles errors when unwrapping Responses", async () => {
@@ -204,11 +206,13 @@ describe("navigations", () => {
204206
})
205207
);
206208
expect(t.router.state.loaderData).toEqual({});
207-
expect(t.router.state.errors).toMatchInlineSnapshot(`
208-
{
209-
"root": [SyntaxError: Unexpected token } in JSON at position 15],
210-
}
211-
`);
209+
210+
// Node 16/18 versus 20 output different errors here :/
211+
let expected =
212+
process.version.startsWith("v16") || process.version.startsWith("v18")
213+
? "Unexpected token } in JSON at position 15"
214+
: "Unexpected non-whitespace character after JSON at position 15";
215+
expect(t.router.state.errors?.root).toEqual(new SyntaxError(expected));
212216
});
213217

214218
it("does not fetch unchanging layout data", async () => {

0 commit comments

Comments
 (0)