From 18700f06ee68a9d4159486b08f413dde5ed0a794 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 22 Jun 2023 14:29:52 -0400 Subject: [PATCH 1/5] Add missing
prop --- .changeset/form-state-prop.md | 5 +++ docs/components/form.md | 19 +++++++++ docs/hooks/use-submit.md | 3 ++ .../__tests__/data-browser-router-test.tsx | 40 +++++++++++++++++++ packages/react-router-dom/dom.ts | 5 +++ packages/react-router-dom/index.tsx | 8 ++++ 6 files changed, 80 insertions(+) create mode 100644 .changeset/form-state-prop.md diff --git a/.changeset/form-state-prop.md b/.changeset/form-state-prop.md new file mode 100644 index 0000000000..fdedef6237 --- /dev/null +++ b/.changeset/form-state-prop.md @@ -0,0 +1,5 @@ +--- +"react-router-dom": patch +--- + +Add missing `` prop to populate `history.state` on submission navigations diff --git a/docs/components/form.md b/docs/components/form.md index b91c636c6a..27329eb8fd 100644 --- a/docs/components/form.md +++ b/docs/components/form.md @@ -215,6 +215,24 @@ See also: - [`useActionData`][useactiondata] - [`useSubmit`][usesubmit] +## `state` + +The `state` property can be used to set a stateful value for the new location which is stored inside [history state][history-state]. This value can subsequently be accessed via `useLocation()`. + +```tsx + +``` + +You can access this state value while on the "new-path" route: + +```ts +let { state } = useLocation(); +``` + ## `preventScrollReset` If you are using [``][scrollrestoration], this lets you prevent the scroll position from being reset to the top of the window when the form action redirects to a new location. @@ -330,3 +348,4 @@ You can access those values from the `request.url` [pickingarouter]: ../routers/picking-a-router [scrollrestoration]: ./scroll-restoration [link-preventscrollreset]: ./link#preventscrollreset +[history-state]: https://developer.mozilla.org/en-US/docs/Web/API/History/state diff --git a/docs/hooks/use-submit.md b/docs/hooks/use-submit.md index 77838c1ec1..a186eb5479 100644 --- a/docs/hooks/use-submit.md +++ b/docs/hooks/use-submit.md @@ -134,4 +134,7 @@ submit(null, { ; ``` +Because submissions are navigations, the options may also contain the other navigation related props from [``][form] such as `replace`, `state`, `preventScrollReset`, `relative`, etc. + [pickingarouter]: ../routers/picking-a-router +[form]: ../components/form.md diff --git a/packages/react-router-dom/__tests__/data-browser-router-test.tsx b/packages/react-router-dom/__tests__/data-browser-router-test.tsx index 96d58d30d5..f9a7e8b7fa 100644 --- a/packages/react-router-dom/__tests__/data-browser-router-test.tsx +++ b/packages/react-router-dom/__tests__/data-browser-router-test.tsx @@ -1541,6 +1541,46 @@ function testDomRouter( `); }); + it("supports ", async () => { + let testWindow = getWindow("/"); + let router = createTestRouter( + [ + { + path: "/", + Component() { + return ( + + + + ); + }, + }, + { + path: "/action", + action: () => null, + Component() { + let state = useLocation().state; + return

{JSON.stringify(state)}

; + }, + }, + ], + { window: testWindow } + ); + let { container } = render(); + expect(testWindow.history.state.usr).toBeUndefined(); + + fireEvent.click(screen.getByText("Submit")); + await waitFor(() => screen.getByText('{"key":"value"}')); + expect(getHtml(container)).toMatchInlineSnapshot(` + "
+

+ {"key":"value"} +

+
" + `); + expect(testWindow.history.state.usr).toEqual({ key: "value" }); + }); + it("supports
", async () => { let actionSpy = jest.fn(); let router = createTestRouter( diff --git a/packages/react-router-dom/dom.ts b/packages/react-router-dom/dom.ts index cf0700edeb..16c9dc0548 100644 --- a/packages/react-router-dom/dom.ts +++ b/packages/react-router-dom/dom.ts @@ -161,6 +161,11 @@ export interface SubmitOptions { */ replace?: boolean; + /** + * State object to add to the history stack entry for this navigation + */ + state?: any; + /** * Determines whether the form action is relative to the route hierarchy or * the pathname. Use this if you want to opt out of navigating the route diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index dc9edbeca8..9648cf9548 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -730,6 +730,11 @@ export interface FormProps extends React.FormHTMLAttributes { */ replace?: boolean; + /** + * State object to add to the history stack entry for this navigation + */ + state?: any; + /** * Determines whether the form action is relative to the route hierarchy or * the pathname. Use this if you want to opt out of navigating the route @@ -784,6 +789,7 @@ const FormImpl = React.forwardRef( { reloadDocument, replace, + state, method = defaultMethod, action, onSubmit, @@ -812,6 +818,7 @@ const FormImpl = React.forwardRef( submit(submitter || event.currentTarget, { method: submitMethod, replace, + state, relative, preventScrollReset, }); @@ -1068,6 +1075,7 @@ export function useSubmit(): SubmitFunction { formMethod: options.method || (method as HTMLFormMethod), formEncType: options.encType || (encType as FormEncType), replace: options.replace, + state: options.state, fromRouteId: currentRouteId, }); }, From 4b9d56333608c5c11c0f52adc6d4220e3d220ca5 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 22 Jun 2023 17:41:41 -0400 Subject: [PATCH 2/5] Bump bundle --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 05550524b8..00636c56a0 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "none": "16.2 kB" }, "packages/react-router-dom/dist/react-router-dom.production.min.js": { - "none": "12.5 kB" + "none": "12.6 kB" }, "packages/react-router-dom/dist/umd/react-router-dom.production.min.js": { "none": "18.6 kB" From b35ce19289e790bb5088833440ebeea54a3c6c4a Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 30 Jun 2023 15:48:37 -0400 Subject: [PATCH 3/5] Disallow state on fetcher.submit --- packages/react-router-dom/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index 9648cf9548..237ab1bd48 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -1036,8 +1036,8 @@ export interface SubmitFunction { export interface FetcherSubmitFunction { ( target: SubmitTarget, - // Fetchers cannot replace because they are not navigation events - options?: Omit + // Fetchers cannot replace or set state because they are not navigation events + options?: Omit ): void; } From 3af9a489ff807f84b2d2ff90aca2b40d17a47ebf Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 30 Jun 2023 16:03:47 -0400 Subject: [PATCH 4/5] Remove replace/state/reloadDocument from fetcher.Form props --- packages/react-router-dom/index.tsx | 41 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index 237ab1bd48..25dd9cbd4b 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -697,7 +697,8 @@ if (__DEV__) { NavLink.displayName = "NavLink"; } -export interface FormProps extends React.FormHTMLAttributes { +export interface FetcherFormProps + extends React.FormHTMLAttributes { /** * The HTTP verb to use when the form is submit. Supports "get", "post", * "put", "delete", "patch". @@ -718,23 +719,6 @@ export interface FormProps extends React.FormHTMLAttributes { */ action?: string; - /** - * Forces a full document navigation instead of a fetch. - */ - reloadDocument?: boolean; - - /** - * Replaces the current entry in the browser history stack when the form - * navigates. Use this if you don't want the user to be able to click "back" - * to the page with the form on it. - */ - replace?: boolean; - - /** - * State object to add to the history stack entry for this navigation - */ - state?: any; - /** * Determines whether the form action is relative to the route hierarchy or * the pathname. Use this if you want to opt out of navigating the route @@ -755,6 +739,25 @@ export interface FormProps extends React.FormHTMLAttributes { onSubmit?: React.FormEventHandler; } +export interface FormProps extends FetcherFormProps { + /** + * Forces a full document navigation instead of a fetch. + */ + reloadDocument?: boolean; + + /** + * Replaces the current entry in the browser history stack when the form + * navigates. Use this if you don't want the user to be able to click "back" + * to the page with the form on it. + */ + replace?: boolean; + + /** + * State object to add to the history stack entry for this navigation + */ + state?: any; +} + /** * A `@remix-run/router`-aware ``. It behaves like a normal form except * that the interaction with the server is with `fetch` instead of new document @@ -1175,7 +1178,7 @@ export function useFormAction( } function createFetcherForm(fetcherKey: string, routeId: string) { - let FetcherForm = React.forwardRef( + let FetcherForm = React.forwardRef( (props, ref) => { let submit = useSubmitFetcher(fetcherKey, routeId); return ; From fdb04ccc8d3628f07e991136cb2b169dc618d7cf Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 30 Jun 2023 16:14:07 -0400 Subject: [PATCH 5/5] Bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8570395e57..26d65783e7 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "none": "16.2 kB" }, "packages/react-router-dom/dist/react-router-dom.production.min.js": { - "none": "12.7 kB" + "none": "12.8 kB" }, "packages/react-router-dom/dist/umd/react-router-dom.production.min.js": { "none": "18.7 kB"