Skip to content

Commit e5c7781

Browse files
committed
Merge branch 'dev' into v2
2 parents c3c85aa + d257ee0 commit e5c7781

File tree

154 files changed

+1690
-761
lines changed

Some content is hidden

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

154 files changed

+1690
-761
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/react": patch
3+
---
4+
5+
Deprecate `fetcher.type` and `fetcher.submission` for Remix v2

.changeset/dirty-wolves-knock.md

-5
This file was deleted.

.changeset/four-feet-grab.md

-5
This file was deleted.
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/server-runtime": patch
3+
---
4+
5+
Don't log server errors for aborted requests as that is an expected flow

.changeset/three-cheetahs-lick.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@remix-run/dev": patch
3+
"@remix-run/react": patch
4+
"@remix-run/server-runtime": patch
5+
---
6+
7+
Deprecate `CatchBoundary` in favor of `future.v2_errorBoundary`

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ tsconfig.tsbuildinfo
3131
/scripts/playground/template/package-lock.json
3232

3333
/NOTES.md
34+
/RELEASENOTES.md

contributors.yml

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
- bsharrow
6666
- bsides
6767
- bustamantedev
68+
- buzinas
6869
- c43721
6970
- camiaei
7071
- CanRau
@@ -358,6 +359,7 @@
358359
- nexxeln
359360
- ni554n
360361
- nicholaschiang
362+
- nicksrandall
361363
- nickytonline
362364
- niconiahi
363365
- nielsdb97
@@ -460,6 +462,7 @@
460462
- tjefferson08
461463
- tombiju
462464
- tombyrer
465+
- TomerAberbach
463466
- toyozaki
464467
- turkerdev
465468
- tvanantwerp

docs/file-conventions/route-files-v2.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ If you want one of the special characters Remix uses for these route conventions
307307

308308
## Folders for Organization
309309

310-
Routes can also be folders with a conventional node module resolution `index.tsx` file inside defining the route module. The rest of the files in the folder will not become routes. This allows you to organize your code closer to the routes that use them instead of repeating the feature names across other folders.
310+
Routes can also be folders with a `route.tsx` file inside defining the route module. The rest of the files in the folder will not become routes. This allows you to organize your code closer to the routes that use them instead of repeating the feature names across other folders.
311311

312312
<docs-info>The files inside a folder have no meaning for the route paths, the route path is completely defined by the folder name</docs-info>
313313

docs/guides/optimistic-ui.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ Remix can help you build optimistic UI with [`useNavigation`][use-navigation] an
1111
## Strategy
1212

1313
1. User submits a form (or you do with [`useSubmit`][use-submit] or [`fetcher.submit`][fetcher-submission]).
14-
2. Remix makes the submission and its data immediately available to you on [`navigation.formData`][navigation-formdata] or [`fetcher.submission`][fetcher-submission].
15-
3. App uses [`submission.formData`][form-data] to render an optimistic version of _what it will render_ when the submission completes successfully.
14+
2. Remix makes the submission and its data immediately available to you on [`navigation.formData`][navigation-formdata] or [`fetcher.formData`][fetcher-submission].
15+
3. App uses [`formData`][form-data] to render an optimistic version of _what it will render_ when the submission completes successfully.
1616
4. Remix automatically revalidates all the data.
1717
- If successful, the user doesn't even notice.
1818
- If it fails, the page data is automatically in sync with the server so the UI reverts automatically.

docs/hooks/use-fetcher.md

+59-20
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ function SomeComponent() {
4444

4545
// build UI with these
4646
fetcher.state;
47-
fetcher.type;
48-
fetcher.submission;
47+
fetcher.formMethod;
48+
fetcher.formAction;
49+
fetcher.formData;
50+
fetcher.formEncType;
4951
fetcher.data;
5052
}
5153
```
@@ -71,6 +73,8 @@ You can know the state of the fetcher with `fetcher.state`. It will be one of:
7173

7274
#### `fetcher.type`
7375

76+
<docs-error>`fetcher.type` is deprecated and will be removed in v2.</docs-error>
77+
7478
This is the type of state the fetcher is in. It's like `fetcher.state`, but more granular. Depending on the fetcher's state, the types can be the following:
7579

7680
- `state === "idle"`
@@ -89,8 +93,47 @@ This is the type of state the fetcher is in. It's like `fetcher.state`, but more
8993
- **actionRedirect** - The action from an "actionSubmission" returned a redirect and the page is transitioning to the new location.
9094
- **normalLoad** - A route's loader is being called without a submission (`fetcher.load()`).
9195

96+
##### Moving away from `fetcher.type`
97+
98+
The `type` field has been been deprecated and will be removed in v2. We've found that `state` is sufficient for almost all use-cases, and when it's not you can derive sub-types via `fetcher.state` and other fields. Here's a few examples:
99+
100+
```js
101+
function Component() {
102+
let fetcher = useFetcher();
103+
104+
let isDone =
105+
fetcher.state === "idle" && fetcher.data != null;
106+
107+
let isActionSubmission = fetcher.state === "submitting";
108+
109+
let isActionReload =
110+
fetcher.state === "loading" &&
111+
fetcher.formMethod != null &&
112+
fetcher.formMethod != "get" &&
113+
// If we returned data, we must be reloading
114+
fetcher.data != null;
115+
116+
let isActionRedirect =
117+
fetcher.state === "loading" &&
118+
fetcher.formMethod != null &&
119+
navigation.formMethod != "get" &&
120+
// If we have no data we must have redirected
121+
fetcher.data == null;
122+
123+
let isLoaderSubmission =
124+
navigation.state === "loading" &&
125+
navigation.state.formMethod === "get";
126+
127+
let isNormalLoad =
128+
navigation.state === "loading" &&
129+
navigation.state.formMethod == null;
130+
}
131+
```
132+
92133
#### `fetcher.submission`
93134

135+
<docs-error>`fetcher.submission` is deprecated and will be removed in v2. Instead, the fields inside of `submission` have been flattened onto the `fetcher` itself (`fetcher.formMethod`, `fetcher.formAction`, `fetcher.formData`, `fetcher.formEncType`)</docs-error>
136+
94137
When using `<fetcher.Form>` or `fetcher.submit()`, the form submission is available to build optimistic UI.
95138

96139
It is not available when the fetcher state is "idle" or "loading".
@@ -153,7 +196,7 @@ function SomeComponent() {
153196
const fetcher = useFetcher();
154197

155198
useEffect(() => {
156-
if (fetcher.type === "init") {
199+
if (fetcher.state === "idle" && fetcher.data == null) {
157200
fetcher.load("/some/route");
158201
}
159202
}, [fetcher]);
@@ -204,7 +247,10 @@ function NewsletterSignup() {
204247
const ref = useRef();
205248

206249
useEffect(() => {
207-
if (newsletter.type === "done" && newsletter.data.ok) {
250+
if (
251+
newsletter.state === "idle" &&
252+
newsletter.data?.ok
253+
) {
208254
ref.current.reset();
209255
}
210256
}, [newsletter]);
@@ -225,7 +271,7 @@ function NewsletterSignup() {
225271
</button>
226272
</p>
227273

228-
{newsletter.type === "done" ? (
274+
{newsletter.state === "idle" && newsletter.data ? (
229275
newsletter.data.ok ? (
230276
<p>Thanks for subscribing!</p>
231277
) : newsletter.data.error ? (
@@ -283,18 +329,12 @@ export function NewsletterSignup() {
283329
Form={newsletter.Form}
284330
data={newsletter.data}
285331
state={newsletter.state}
286-
type={newsletter.type}
287332
/>
288333
);
289334
}
290335

291336
// used here and in the route
292-
export function NewsletterForm({
293-
Form,
294-
data,
295-
state,
296-
type,
297-
}) {
337+
export function NewsletterForm({ Form, data, state }) {
298338
// refactor a bit in here, just read from props instead of useFetcher
299339
}
300340
```
@@ -309,12 +349,7 @@ import { NewsletterForm } from "~/NewsletterSignup";
309349
export default function NewsletterSignupRoute() {
310350
const data = useActionData<typeof action>();
311351
return (
312-
<NewsletterForm
313-
Form={Form}
314-
data={data}
315-
state="idle"
316-
type="done"
317-
/>
352+
<NewsletterForm Form={Form} data={data} state="idle" />
318353
);
319354
}
320355
```
@@ -355,7 +390,11 @@ function UserAvatar({ partialUser }) {
355390
const [showDetails, setShowDetails] = useState(false);
356391

357392
useEffect(() => {
358-
if (showDetails && userDetails.type === "init") {
393+
if (
394+
showDetails &&
395+
userDetails.state === "idle" &&
396+
!userDetails.data
397+
) {
359398
userDetails.load(`/users/${user.id}/details`);
360399
}
361400
}, [showDetails, userDetails]);
@@ -367,7 +406,7 @@ function UserAvatar({ partialUser }) {
367406
>
368407
<img src={partialUser.profileImageUrl} />
369408
{showDetails ? (
370-
userDetails.type === "done" ? (
409+
userDetails.state === "idle" && userDetails.data ? (
371410
<UserPopup user={userDetails.data} />
372411
) : (
373412
<UserPopupLoading />

docs/hooks/use-fetchers.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ For example, imagine a UI where the sidebar lists projects, and the main view di
2727
+-----------------+----------------------------┘
2828
```
2929

30-
When the user clicks a checkbox, the submission goes to the action to change the state of the task. Instead of creating a "loading state" we want to create an "optimistic UI" that will **immediately** update the checkbox to appear checked even though the server hasn't processed it yet. In the checkbox component, we can use `fetcher.submission`:
30+
When the user clicks a checkbox, the submission goes to the action to change the state of the task. Instead of creating a "loading state" we want to create an "optimistic UI" that will **immediately** update the checkbox to appear checked even though the server hasn't processed it yet. In the checkbox component, we can use `fetcher.formData`:
3131

3232
```tsx
3333
function Task({ task }) {
3434
const toggle = useFetcher();
35-
const checked = toggle.submission
35+
const checked = toggle.formData
3636
? // use the optimistic version
37-
Boolean(toggle.submission.formData.get("complete"))
37+
Boolean(toggle.formData.get("complete"))
3838
: // use the normal version
3939
task.complete;
4040

@@ -81,7 +81,7 @@ This is where `useFetchers` comes in. Up in the sidebar, we can access all the i
8181
The strategy has three steps:
8282

8383
1. Find the submissions for tasks in a specific project
84-
2. Use the `fetcher.submission.formData` to immediately update the count
84+
2. Use the `fetcher.formData` to immediately update the count
8585
3. Use the normal task's state if it's not inflight
8686

8787
Here's some sample code:
@@ -95,15 +95,15 @@ function ProjectTaskCount({ project }) {
9595
const myFetchers = new Map();
9696
for (const f of fetchers) {
9797
if (
98-
f.submission &&
99-
f.submission.action.startsWith(
98+
f.formAction &&
99+
f.formAction.startsWith(
100100
`/projects/${project.id}/task`
101101
)
102102
) {
103-
const taskId = f.submission.formData.get("id");
103+
const taskId = f.formData.get("id");
104104
myFetchers.set(
105105
parseInt(taskId),
106-
f.submission.formData.get("complete") === "on"
106+
f.formData.get("complete") === "on"
107107
);
108108
}
109109
}

docs/hooks/use-transition.md

+56-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ title: useTransition
44

55
# `useTransition`
66

7+
<docs-error>This API will be removed in v2 in favor of [`useNavigation`][use-navigation]. You can start using the new `useNavigation` hook today to make upgrading in the future easy, but you can keep using `useTransition` until v2.</docs-error>
8+
9+
---
10+
711
<docs-success>Watch the <a href="https://www.youtube.com/playlist?list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">📼 Remix Singles</a>: <a href="https://www.youtube.com/watch?v=y4VLIFjFq8k&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Pending UI</a>, <a href="https://www.youtube.com/watch?v=bMLej7bg5Zo&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Clearing Inputs After Form Submissions</a>, and <a href="https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Optimistic UI</a></docs-success>
812

913
This hook tells you everything you need to know about a page transition to build pending navigation indicators and optimistic UI on data mutations. Things like:
@@ -114,11 +118,62 @@ function SubmitButton() {
114118
}
115119
```
116120

121+
### Moving away from `transition.type`
122+
123+
The `type` field has been removed in the new `useNavigation` hook (which will replace `useTransition` in Remix v2). We've found that `state` is sufficient for almost all use-cases, and when it's not you can derive sub-types via `navigation.state` and other fields. Also note that the `loaderSubmission` type is now represented with `state: "loading"`. Here's a few examples:
124+
125+
```js
126+
function Component() {
127+
let navigation = useNavigation();
128+
129+
let isActionSubmission =
130+
navigation.state === "submitting";
131+
132+
let isActionReload =
133+
navigation.state === "loading" &&
134+
navigation.formMethod != null &&
135+
navigation.formMethod != "get" &&
136+
// We had a submission navigation and are loading the submitted location
137+
navigation.formAction === navigation.pathname;
138+
139+
let isActionRedirect =
140+
navigation.state === "loading" &&
141+
navigation.formMethod != null &&
142+
navigation.formMethod != "get" &&
143+
// We had a submission navigation and are now navigating to different location
144+
navigation.formAction !== navigation.pathname;
145+
146+
let isLoaderSubmission =
147+
navigation.state === "loading" &&
148+
navigation.state.formMethod === "get" &&
149+
// We had a loader submission and are navigating to the submitted location
150+
navigation.formAction === navigation.pathname;
151+
152+
let isLoaderSubmissionRedirect =
153+
navigation.state === "loading" &&
154+
navigation.state.formMethod === "get" &&
155+
// We had a loader submission and are navigating to a new location
156+
navigation.formAction !== navigation.pathname;
157+
}
158+
```
159+
117160
## `transition.submission`
118161

119162
Any transition that started from a `<Form>` or `useSubmit` will have your form's submission attached to it. This is primarily useful to build "Optimistic UI" with the `submission.formData` [`FormData`][form-data] object.
120163

121-
TODO: Example
164+
### Moving away from `transition.submission`
165+
166+
The `submission` field has been removed in the new `useNavigation` hook (which will replace `useTransition` in Remix v2) and the same sub-fields are now exposed directly on the `navigation`:
167+
168+
```js
169+
function Component() {
170+
let navigation = useNavigation();
171+
// navigation.formMethod
172+
// navigation.formAction
173+
// navigation.formData
174+
// navigation.formEncType
175+
}
176+
```
122177

123178
## `transition.location`
124179

@@ -149,10 +204,6 @@ function PendingLink({ to, children }) {
149204

150205
Note that this link will not appear "pending" if a form is being submitted to the URL the link points to, because we only do this for "loading" states. The form will contain the pending UI for when the state is "submitting", once the action is complete, then the link will go pending.
151206

152-
## v2 deprecation
153-
154-
This API will be removed in v2 in favor of [`useNavigation`][use-navigation]. You can start using the new `useNavigation` hook today to make upgrading in the future easy, but you can keep using `useTransition` before v2.
155-
156207
[usefetcher]: ./use-fetcher
157208
[form-data]: https://developer.mozilla.org/en-US/docs/Web/API/FormData
158209
[use-navigation]: ./use-navigation

docs/route/catch-boundary.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ title: CatchBoundary
44

55
# `CatchBoundary`
66

7+
<docs-warning>The separation of `CatchBoundary` and `ErrorBoundary` has been deprecated and Remix v2 will use a singular `ErrorBoundary` for all thrown Responses and Errors. It is recommended that you opt-into the new behavior in Remix v1 via the `future.v2_errorBoundary` flag in your `remix.config.js` file. Please refer to the [ErrorBoundary (v2)][error-boundary-v2] docs for more information.</docs-warning>
8+
79
A `CatchBoundary` is a React component that renders whenever an action or loader throws a `Response`.
810

911
**Note:** We use the word "catch" to represent the codepath taken when a `Response` type is thrown; you thought about bailing from the "happy path". This is different from an uncaught error you did not expect to occur.
@@ -29,3 +31,5 @@ export function CatchBoundary() {
2931
);
3032
}
3133
```
34+
35+
[error-boundary-v2]: ./error-boundary-v2

docs/route/error-boundary-v2.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: ErrorBoundary (v2)
3+
---
4+
5+
# `ErrorBoundary (v2)`
6+
7+
<docs-info>You can opt into the Remix v2 `ErrorBoundary` behavior via the `future.v2_errorBoundary` flag in your `remix.config.js`</docs-info>
8+
9+
If you export an `ErrorBoundary` component from your route module, it will be used as the React Router [`errorElement`][rr-error-element] and will render if you throw from a loader/action or if React throws during rendering your Route component.
10+
11+
[rr-error-element]: https://reactrouter.com/route/error-element

0 commit comments

Comments
 (0)