Skip to content

Commit 1638429

Browse files
committed
Merge branch 'main' into release-next
2 parents f353a6f + a0e6be5 commit 1638429

File tree

67 files changed

+467
-117
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+467
-117
lines changed

contributors.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- AmRo045
1515
- amsal
1616
- andreiduca
17+
- antonmontrezor
1718
- appden
1819
- arnassavickas
1920
- aroyan
@@ -124,6 +125,7 @@
124125
- lordofthecactus
125126
- LordThi
126127
- loun4
128+
- lounsbrough
127129
- lpaube
128130
- lqze
129131
- lukerSpringTree
@@ -191,6 +193,7 @@
191193
- thisiskartik
192194
- thomasverleye
193195
- ThornWu
196+
- tiborbarsi
194197
- timdorr
195198
- TkDodo
196199
- tkindy

docs/components/routes.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface RoutesProps {
1717
</Routes>;
1818
```
1919

20-
<docs-info>If you're using a data router like [`createBrowserRouter`][createbrowserrouter] it is uncommon to use this component as it does not participate in data loading.</docs-info>
20+
<docs-info>If you're using a data router like [`createBrowserRouter`][createbrowserrouter] it is uncommon to use this component as routes defined as part of a descendant `<Routes>` tree cannot leverage the [Data APIs][data-apis] available to [`RouterProvider`][router-provider] apps. You **can and should** use this component within your `RouterProvider` application [while you are migrating][migrating-to-router-provider].</docs-info>
2121

2222
Whenever the location changes, `<Routes>` looks through all its child routes to find the best match and renders that branch of the UI. `<Route>` elements may be nested to indicate nested UI, which also correspond to nested URL paths. Parent routes render their child routes by rendering an [`<Outlet>`][outlet].
2323

@@ -38,3 +38,6 @@ Whenever the location changes, `<Routes>` looks through all its child routes to
3838
[outlet]: ./outlet
3939
[use-route]: ../hooks/use-routes
4040
[createbrowserrouter]: ../routers/create-browser-router
41+
[data-apis]: ../routers/picking-a-router#data-apis
42+
[router-provider]: ../routers/router-provider
43+
[migrating-to-router-provider]: ../upgrading/v6-data

docs/guides/ssr.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Once we've sent the HTML back to the browser, we'll need to "hydrate" the applic
143143

144144
```jsx filename=entry-client.jsx lines=[10-15]
145145
import * as React from "react";
146-
import ReactDOM from "react-dom/client";
146+
import * as ReactDOM from "react-dom/client";
147147
import {
148148
createBrowserRouter,
149149
RouterProvider,
@@ -275,7 +275,7 @@ app.listen(3000);
275275
And finally, you'll need a similar file to "hydrate" the app with your JavaScript bundle that includes the very same `App` component. Note the use of `BrowserRouter` instead of `StaticRouter`.
276276
277277
```js filename=client.entry.js
278-
import ReactDOM from "react-dom";
278+
import * as ReactDOM from "react-dom";
279279
import { BrowserRouter } from "react-router-dom";
280280
import App from "./App";
281281

docs/hooks/use-navigate.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: useNavigate
44

55
# `useNavigate`
66

7-
<docs-warning>It's usually better to use [`redirect`][redirect] in loaders and actions than this hook</docs-warning>
7+
<docs-warning>It's usually better to use [`redirect`][redirect] in [`loaders`][loaders] and [`actions`][actions] than this hook</docs-warning>
88

99
The `useNavigate` hook returns a function that lets you navigate programmatically, for example in an effect:
1010

@@ -50,3 +50,5 @@ The `navigate` function has two signatures:
5050
If using `replace: true`, the navigation will replace the current entry in the history stack instead of adding a new one.
5151

5252
[redirect]: ../fetch/redirect
53+
[loaders]: ../route/loader
54+
[actions]: ../route/action

docs/route/action.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,62 @@ You can `throw` in your action to break out of the current call stack (stop runn
129129

130130
For more details and expanded use cases, read the [errorElement][errorelement] documentation.
131131

132+
## Handling multiple actions per route
133+
134+
A fairly common question that pops up is _"What if I need to handle multiple different behaviors in my action?"_ There's a few ways to accomplish this, but usually the simplest is to put a `name`/`value` on your `<button type="submit">` and use that in the action to decide which code to execute (that's right - submitting [buttons][button] can have name/value attributes!):
135+
136+
```jsx lines=[3,5,10,30-32,42-44]
137+
async function action({ request }) {
138+
let formData = await request.formData();
139+
let intent = formData.get("intent");
140+
141+
if (intent === "edit") {
142+
await editSong(formData);
143+
return { ok: true };
144+
}
145+
146+
if (intent === "add") {
147+
await addSong(formData);
148+
return { ok: true };
149+
}
150+
151+
throw json(
152+
{ message: "Invalid intent" },
153+
{ status: 400 }
154+
);
155+
}
156+
157+
function Component() {
158+
let song = useLoaderData();
159+
160+
// When the song exists, show an edit form
161+
if (song) {
162+
return (
163+
<Form method="post">
164+
<p>Edit song lyrics:</p>
165+
{/* Edit song inputs */}
166+
<button type="submit" name="intent" value="edit">
167+
Edit
168+
</button>
169+
</Form>
170+
);
171+
}
172+
173+
// Otherwise show a form to add a new song
174+
return (
175+
<Form method="post">
176+
<p>Add new lyrics:</p>
177+
{/* Add song inputs */}
178+
<button type="submit" name="intent" value="add">
179+
Add
180+
</button>
181+
</Form>
182+
);
183+
}
184+
```
185+
186+
If a button name/value isn't right for your use case, you could also use a hidden input to send and `intent` or you could submit different HTTP methods via the [`<Form method>`][form-method] prop (`POST` for add, `PUT`/`PATCH` for edit, `DELETE` for remove).
187+
132188
[loader]: ./loader
133189
[pickingarouter]: ../routers/picking-a-router
134190
[dynamicsegments]: ./route#dynamic-segments
@@ -146,3 +202,5 @@ For more details and expanded use cases, read the [errorElement][errorelement] d
146202
[useactiondata]: ../hooks/use-action-data
147203
[returningresponses]: ./loader#returning-responses
148204
[createbrowserrouter]: ../routers/create-browser-router
205+
[button]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
206+
[form-method]: ../components/form#method

docs/start/_tutorial.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Actually, that "!" doesn't look boring at all. This is pretty exciting. We sat o
7474
Finally, go make sure `index.js` or `main.jsx` (depending on the bundler you used) is actually boring:
7575

7676
```tsx filename=src/main.jsx
77-
import ReactDOM from "react-dom/client";
77+
import * as ReactDOM from "react-dom/client";
7878
import App from "./App";
7979

8080
const root = ReactDOM.createRoot(
@@ -98,7 +98,7 @@ npm run dev
9898
First things first, we want to connect your app to the browser's URL: import `BrowserRouter` and render it around your whole app.
9999

100100
```tsx lines=[2,9-11] filename=src/main.jsx
101-
import ReactDOM from "react-dom/client";
101+
import * as ReactDOM from "react-dom/client";
102102
import { BrowserRouter } from "react-router-dom";
103103
import App from "./App";
104104

@@ -177,7 +177,7 @@ export default function Invoices() {
177177
Finally, let's teach React Router how to render our app at different URLs by creating our first "Route Config" inside of `main.jsx` or `index.js`.
178178

179179
```tsx lines=[2,4-5,8-9,15-21] filename=src/main.jsx
180-
import ReactDOM from "react-dom/client";
180+
import * as ReactDOM from "react-dom/client";
181181
import {
182182
BrowserRouter,
183183
Routes,
@@ -217,7 +217,7 @@ Let's get some automatic, persistent layout handling by doing just two things:
217217
First let's nest the routes. Right now the expenses and invoices routes are siblings to the app, we want to make them _children_ of the app route:
218218

219219
```jsx lines=[17-20] filename=src/main.jsx
220-
import ReactDOM from "react-dom/client";
220+
import * as ReactDOM from "react-dom/client";
221221
import {
222222
BrowserRouter,
223223
Routes,

docs/start/overview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This enables faster user experiences because the browser doesn't need to request
1818
Client side routing is enabled by creating a `Router` and linking/submitting to pages with `Link` and `<Form>`:
1919

2020
```jsx [10,16,27]
21-
import React from "react";
21+
import * as React from "react";
2222
import { createRoot } from "react-dom/client";
2323
import {
2424
createBrowserRouter,
@@ -78,7 +78,7 @@ createBrowserRouter(
7878
element={<Login />}
7979
loader={redirectIfUser}
8080
/>
81-
<Route path="logout" />
81+
<Route path="logout" action={logoutUser} />
8282
</Route>
8383
</Route>
8484
)

docs/start/tutorial.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ The `main.jsx` file is the entry point. Open it up and we'll put React Router on
6868
👉 **Create and render a [browser router][createbrowserrouter] in `main.jsx`**
6969

7070
```jsx lines=[3-6,9-14,18] filename=src/main.jsx
71-
import React from "react";
72-
import ReactDOM from "react-dom/client";
71+
import * as React from "react";
72+
import * as ReactDOM from "react-dom/client";
7373
import {
7474
createBrowserRouter,
7575
RouterProvider,
@@ -1487,10 +1487,19 @@ You could certainly do this as a controlled component, but you'll end up with mo
14871487

14881488
Notice how controlling the input requires three points of synchronization now instead of just one. The behavior is identical but the code is more complex.
14891489

1490-
```jsx filename=src/routes/root.jsx lines=[1,6,9-11,25-28]
1490+
```jsx filename=src/routes/root.jsx lines=[1,6,15,18-20,34-37]
14911491
import { useEffect, useState } from "react";
14921492
// existing code
14931493

1494+
export async function loader({ request }) {
1495+
const url = new URL(request.url);
1496+
const q = url.searchParams.get("q") || "";
1497+
const contacts = await getContacts(q);
1498+
return { contacts, q };
1499+
}
1500+
1501+
// existing code
1502+
14941503
export default function Root() {
14951504
const { contacts, q } = useLoaderData();
14961505
const [query, setQuery] = useState(q);
@@ -1911,6 +1920,7 @@ And for our final trick, many folks prefer to configure their routes with JSX. Y
19111920
import {
19121921
createRoutesFromElements,
19131922
createBrowserRouter,
1923+
Route,
19141924
} from "react-router-dom";
19151925

19161926
const router = createBrowserRouter(

docs/upgrading/v5.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Upgrading from v5
3-
order: 1
3+
order: 2
44
---
55

66
# Upgrading from v5

0 commit comments

Comments
 (0)