Skip to content

Commit 6c6f17c

Browse files
committed
Merge branch 'release-next'
2 parents 4c26649 + 1acea8b commit 6c6f17c

File tree

23 files changed

+758
-81
lines changed

23 files changed

+758
-81
lines changed

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,4 @@
224224
- yionr
225225
- yuleicul
226226
- zheng-chuang
227+
- istarkov

docs/components/form.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,24 @@ See also:
215215
- [`useActionData`][useactiondata]
216216
- [`useSubmit`][usesubmit]
217217

218+
## `state`
219+
220+
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()`.
221+
222+
```tsx
223+
<Form
224+
method="post"
225+
action="new-path"
226+
state={{ some: "value" }}
227+
/>
228+
```
229+
230+
You can access this state value while on the "new-path" route:
231+
232+
```ts
233+
let { state } = useLocation();
234+
```
235+
218236
## `preventScrollReset`
219237

220238
If you are using [`<ScrollRestoration>`][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`
330348
[pickingarouter]: ../routers/picking-a-router
331349
[scrollrestoration]: ./scroll-restoration
332350
[link-preventscrollreset]: ./link#preventscrollreset
351+
[history-state]: https://developer.mozilla.org/en-US/docs/Web/API/History/state

docs/hooks/use-submit.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,7 @@ submit(null, {
150150
<Form action="/logout" method="post" />;
151151
```
152152

153+
Because submissions are navigations, the options may also contain the other navigation related props from [`<Form>`][form] such as `replace`, `state`, `preventScrollReset`, `relative`, etc.
154+
153155
[pickingarouter]: ../routers/picking-a-router
156+
[form]: ../components/form.md

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
},
110110
"filesize": {
111111
"packages/router/dist/router.umd.min.js": {
112-
"none": "46.6 kB"
112+
"none": "47.2 kB"
113113
},
114114
"packages/react-router/dist/react-router.production.min.js": {
115115
"none": "13.8 kB"
@@ -118,10 +118,10 @@
118118
"none": "16.2 kB"
119119
},
120120
"packages/react-router-dom/dist/react-router-dom.production.min.js": {
121-
"none": "12.6 kB"
121+
"none": "12.8 kB"
122122
},
123123
"packages/react-router-dom/dist/umd/react-router-dom.production.min.js": {
124-
"none": "18.6 kB"
124+
"none": "18.71 kB"
125125
}
126126
}
127127
}

packages/react-router-dom-v5-compat/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# `react-router-dom-v5-compat`
22

3+
## 6.14.2
4+
5+
### Patch Changes
6+
7+
- Updated dependencies:
8+
9+
10+
311
## 6.14.1
412

513
### Patch Changes

packages/react-router-dom-v5-compat/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-router-dom-v5-compat",
3-
"version": "6.14.1",
3+
"version": "6.14.2",
44
"description": "Migration path to React Router v6 from v4/5",
55
"keywords": [
66
"react",
@@ -24,7 +24,7 @@
2424
"types": "./dist/index.d.ts",
2525
"dependencies": {
2626
"history": "^5.3.0",
27-
"react-router": "6.14.1"
27+
"react-router": "6.14.2"
2828
},
2929
"peerDependencies": {
3030
"react": ">=16.8",

packages/react-router-dom/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# `react-router-dom`
22

3+
## 6.14.2
4+
5+
### Patch Changes
6+
7+
- Properly decode element id when emulating hash scrolling via `<ScrollRestoration>` ([#10682](https://github.com/remix-run/react-router/pull/10682))
8+
- Add missing `<Form state>` prop to populate `history.state` on submission navigations ([#10630](https://github.com/remix-run/react-router/pull/10630))
9+
- Support proper hydration of `Error` subclasses such as `ReferenceError`/`TypeError` ([#10633](https://github.com/remix-run/react-router/pull/10633))
10+
- Updated dependencies:
11+
- `@remix-run/[email protected]`
12+
13+
314
## 6.14.1
415

516
### Patch Changes

packages/react-router-dom/__tests__/data-browser-router-test.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,49 @@ function testDomRouter(
328328
`);
329329
});
330330

331+
it("deserializes Error subclass instances from the window", async () => {
332+
window.__staticRouterHydrationData = {
333+
loaderData: {},
334+
actionData: null,
335+
errors: {
336+
"0": {
337+
message: "error message",
338+
__type: "Error",
339+
__subType: "ReferenceError",
340+
},
341+
},
342+
};
343+
let router = createTestRouter(
344+
createRoutesFromElements(
345+
<Route path="/" element={<h1>Nope</h1>} errorElement={<Boundary />} />
346+
)
347+
);
348+
let { container } = render(<RouterProvider router={router} />);
349+
350+
function Boundary() {
351+
let error = useRouteError() as Error;
352+
return error instanceof Error ? (
353+
<>
354+
<pre>{error.toString()}</pre>
355+
<pre>stack:{error.stack}</pre>
356+
</>
357+
) : (
358+
<p>No :(</p>
359+
);
360+
}
361+
362+
expect(getHtml(container)).toMatchInlineSnapshot(`
363+
"<div>
364+
<pre>
365+
ReferenceError: error message
366+
</pre>
367+
<pre>
368+
stack:
369+
</pre>
370+
</div>"
371+
`);
372+
});
373+
331374
it("renders fallbackElement while first data fetch happens", async () => {
332375
let fooDefer = createDeferred();
333376
let router = createTestRouter(
@@ -1541,6 +1584,46 @@ function testDomRouter(
15411584
`);
15421585
});
15431586

1587+
it("supports <Form state>", async () => {
1588+
let testWindow = getWindow("/");
1589+
let router = createTestRouter(
1590+
[
1591+
{
1592+
path: "/",
1593+
Component() {
1594+
return (
1595+
<Form method="post" action="/action" state={{ key: "value" }}>
1596+
<button type="submit">Submit</button>
1597+
</Form>
1598+
);
1599+
},
1600+
},
1601+
{
1602+
path: "/action",
1603+
action: () => null,
1604+
Component() {
1605+
let state = useLocation().state;
1606+
return <p>{JSON.stringify(state)}</p>;
1607+
},
1608+
},
1609+
],
1610+
{ window: testWindow }
1611+
);
1612+
let { container } = render(<RouterProvider router={router} />);
1613+
expect(testWindow.history.state.usr).toBeUndefined();
1614+
1615+
fireEvent.click(screen.getByText("Submit"));
1616+
await waitFor(() => screen.getByText('{"key":"value"}'));
1617+
expect(getHtml(container)).toMatchInlineSnapshot(`
1618+
"<div>
1619+
<p>
1620+
{"key":"value"}
1621+
</p>
1622+
</div>"
1623+
`);
1624+
expect(testWindow.history.state.usr).toEqual({ key: "value" });
1625+
});
1626+
15441627
it("supports <Form reloadDocument={true}>", async () => {
15451628
let actionSpy = jest.fn();
15461629
let router = createTestRouter(

packages/react-router-dom/__tests__/data-static-router-test.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,51 @@ describe("A <StaticRouterProvider>", () => {
701701
);
702702
});
703703

704+
it("serializes Error subclass instances", async () => {
705+
let routes = [
706+
{
707+
path: "/",
708+
loader: () => {
709+
throw new ReferenceError("oh no");
710+
},
711+
},
712+
];
713+
let { query } = createStaticHandler(routes);
714+
715+
let context = (await query(
716+
new Request("http://localhost/", {
717+
signal: new AbortController().signal,
718+
})
719+
)) as StaticHandlerContext;
720+
721+
let html = ReactDOMServer.renderToStaticMarkup(
722+
<React.StrictMode>
723+
<StaticRouterProvider
724+
router={createStaticRouter(routes, context)}
725+
context={context}
726+
/>
727+
</React.StrictMode>
728+
);
729+
730+
// stack is stripped by default from SSR errors
731+
let expectedJsonString = JSON.stringify(
732+
JSON.stringify({
733+
loaderData: {},
734+
actionData: null,
735+
errors: {
736+
"0": {
737+
message: "oh no",
738+
__type: "Error",
739+
__subType: "ReferenceError",
740+
},
741+
},
742+
})
743+
);
744+
expect(html).toMatch(
745+
`<script>window.__staticRouterHydrationData = JSON.parse(${expectedJsonString});</script>`
746+
);
747+
});
748+
704749
it("supports a nonce prop", async () => {
705750
let routes = [
706751
{

packages/react-router-dom/dom.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ export interface SubmitOptions {
171171
*/
172172
replace?: boolean;
173173

174+
/**
175+
* State object to add to the history stack entry for this navigation
176+
*/
177+
state?: any;
178+
174179
/**
175180
* Determines whether the form action is relative to the route hierarchy or
176181
* the pathname. Use this if you want to opt out of navigating the route

0 commit comments

Comments
 (0)