Skip to content

Commit

Permalink
Merge branch 'dev' into fix-vite-ssr-emit-assets
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish committed Nov 15, 2023
2 parents 70f63a6 + bfa2bbc commit d475df4
Show file tree
Hide file tree
Showing 28 changed files with 2,073 additions and 288 deletions.
5 changes: 5 additions & 0 deletions .changeset/eight-jeans-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Fix Vite production builds when plugins that have different local state between `development` and `production` modes are present, e.g. `@mdx-js/rollup`.
2 changes: 1 addition & 1 deletion .changeset/green-buses-suffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@remix-run/dev": patch
---

Handle Vite v5's updated manifest path
Support Vite 5
5 changes: 5 additions & 0 deletions .changeset/hungry-tables-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Allow `process.env.NODE_ENV` values other than `"development"` in Vite dev
5 changes: 5 additions & 0 deletions .changeset/itchy-rabbits-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Attach CSS from shared chunks to routes in Vite build
6 changes: 6 additions & 0 deletions .changeset/slow-eyes-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@remix-run/dev": patch
"@remix-run/express": patch
---

Fix flash of unstyled content on initial page load in Vite dev when using a custom Express server
5 changes: 5 additions & 0 deletions .changeset/stabilize-use-blocker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": minor
---

Remove the `unstable_` prefix from the [`useBlocker`](https://reactrouter.com/en/main/hooks/use-blocker) hook as it's been in use for enough time that we are confident in the API. We do not plan to remove the prefix from `unstable_usePrompt` due to differences in how browsers handle `window.confirm` that prevent React Router from guaranteeing consistent/correct behavior.
5 changes: 5 additions & 0 deletions .changeset/tricky-news-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Populate `process.env` from `.env` files on the server in Vite dev
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- akamfoad
- alanhoskins
- Alarid
- alcpereira
- alex-ketch
- alexmgrant
- alextes
Expand Down
82 changes: 82 additions & 0 deletions docs/hooks/use-blocker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: useBlocker
---

# `useBlocker`

The `useBlocker` hook allows you to prevent the user from navigating away from the current location, and present them with a custom UI to allow them to confirm the navigation.

<docs-info>
This only works for client-side navigations within your React Router application and will not block document requests. To prevent document navigations you will need to add your own <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event" target="_blank">`beforeunload`</a> event handler.
</docs-info>

<docs-warning>
Blocking a user from navigating is a bit of an anti-pattern, so please carefully consider any usage of this hook and use it sparingly. In the de-facto use case of preventing a user navigating away from a half-filled form, you might consider persisting unsaved state to `sessionStorage` and automatically re-filling it if they return instead of blocking them from navigating away.
</docs-warning>

```tsx
function ImportantForm() {
const [value, setValue] = React.useState("");

// Block navigating elsewhere when data has been entered into the input
const blocker = useBlocker(
({ currentLocation, nextLocation }) =>
value !== "" &&
currentLocation.pathname !== nextLocation.pathname
);

return (
<Form method="post">
<label>
Enter some important data:
<input
name="data"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</label>
<button type="submit">Save</button>

{blocker.state === "blocked" ? (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={() => blocker.proceed()}>
Proceed
</button>
<button onClick={() => blocker.reset()}>
Cancel
</button>
</div>
) : null}
</Form>
);
}
```

For a more complete example, please refer to the [example][example] in the repository.

## Properties

### `state`

The current state of the blocker

- `unblocked` - the blocker is idle and has not prevented any navigation
- `blocked` - the blocker has prevented a navigation
- `proceeding` - the blocker is proceeding through from a blocked navigation

### `location`

When in a `blocked` state, this represents the location to which we blocked a navigation. When in a `proceeding` state, this is the location being navigated to after a `blocker.proceed()` call.

## Methods

### `proceed()`

When in a `blocked` state, you may call `blocker.proceed()` to proceed to the blocked location.

### `reset()`

When in a `blocked` state, you may call `blocker.reset()` to return the blocker back to an `unblocked` state and leave the user at the current location.

[example]: https://github.com/remix-run/react-router/tree/main/examples/navigation-blocking
13 changes: 12 additions & 1 deletion docs/hooks/use-fetcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ fetcher.submit(
fetcher.submit(formData);
```

## `fetcher.load(href)`
`fetcher.submit` is a wrapper around a [`useSubmit`][use-submit] call for the fetcher instance, so it also accepts the same options as `useSubmit`.

### `fetcher.load(href, options)`

Loads data from a route loader. While multiple nested routes can match a URL, only the leaf route will be called.

Expand All @@ -96,6 +98,12 @@ fetcher.load("/some/route");
fetcher.load("/some/route?foo=bar");
```

#### `options.unstable_flushSync`

The `unstable_flushSync` option tells React Router DOM to wrap the initial state update for this `fetcher.load` in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.

<docs-warning>`ReactDOM.flushSync` de-optimizes React and can hurt the performance of your app.</docs-warning>

## Properties

### `fetcher.state`
Expand Down Expand Up @@ -146,3 +154,6 @@ The form method of the submission.
[concurrent_mutations_with_use_fetcher]: https://www.youtube.com/watch?v=vTzNpiOk668&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6
[optimistic_ui]: https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6
[use_fetchers]: ./use-fetchers
[flush-sync]: https://react.dev/reference/react-dom/flushSync
[start-transition]: https://react.dev/reference/react/startTransition
[use-submit]: ./use-submit
12 changes: 10 additions & 2 deletions docs/hooks/use-navigate.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,15 @@ navigate(".", {
```

- **replace**: boolean - replace the current entry in the history stack instead of pushing a new one
- **relative**: `"route" | "path"` - defines the relative path behavior for the link. `"route"` will use the route hierarchy so `".."` will remove all URL segments of the current route pattern while `"path"` will use the URL path so `".."` will remove one URL segment.
- **state**: any - adds persistent client side routing state to the next location.
- **relative**: `"route" | "path"` - defines the relative path behavior for the link
- `"route"` will use the route hierarchy so `".."` will remove all URL segments of the current route pattern while `"path"` will use the URL path so `".."` will remove one URL segment
- **state**: any - adds persistent client side routing state to the next location
- **unstable_flushSync**: boolean - wraps the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]
- **unstable_viewTransition**: boolean - enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`
- If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state]

[redirect]: ../fetch/redirect
[flush-sync]: https://react.dev/reference/react-dom/flushSync
[start-transition]: https://react.dev/reference/react/startTransition
[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
[use-view-transition-state]: ../hooks//use-view-transition-state
49 changes: 49 additions & 0 deletions docs/hooks/use-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: unstable_usePrompt
---

# `unstable_usePrompt`

The `unstable_usePrompt` hook allows you to prompt the user for confirmation via [`window.confirm`][window-confirm] prior to navigating away from the current location.

<docs-info>
This only works for client-side navigations within your React Router application and will not block document requests. To prevent document navigations you will need to add your own <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event" target="_blank">`beforeunload`</a> event handler.
</docs-info>

<docs-warning>
Blocking a user from navigating is a bit of an anti-pattern, so please carefully consider any usage of this hook and use it sparingly. In the de-facto use case of preventing a user navigating away from a half-filled form, you might consider persisting unsaved state to `sessionStorage` and automatically re-filling it if they return instead of blocking them from navigating away.
</docs-warning>

<docs-warning>
We do not plan to remove the `unstable_` prefix from this hook because the behavior is non-deterministic across browsers when the prompt is open, so React Router cannot guarantee correct behavior in all scenarios. To avoid this non-determinism, we recommend using `useBlocker` instead which also gives you control over the confirmation UX.
</docs-warning>

```tsx
function ImportantForm() {
const [value, setValue] = React.useState("");

// Block navigating elsewhere when data has been entered into the input
unstable_usePrompt({
message: "Are you sure?",
when: ({ currentLocation, nextLocation }) =>
value !== "" &&
currentLocation.pathname !== nextLocation.pathname,
});

return (
<Form method="post">
<label>
Enter some important data:
<input
name="data"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</label>
<button type="submit">Save</button>
</Form>
);
}
```

[window-confirm]: https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm
7 changes: 7 additions & 0 deletions docs/hooks/use-submit.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ Options for the submission, the same as `<Form>` props. All options are optional
- **preventScrollReset**: Prevents the scroll position from being reset to the top of the window when the data is submitted. Default is `false`.
- **replace**: Replaces the current entry in the history stack, instead of pushing the new entry. Default is `false`.
- **relative**: Defines relative route resolution behavior. Either `"route"` (relative to the route hierarchy) or `"path"` (relative to the URL).
- **unstable_flushSync**: Wraps the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]
- **unstable_viewTransition**: Enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`
- If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state]

```tsx
submit(data, {
Expand All @@ -93,3 +96,7 @@ submit(data, {
[form-vs-fetcher]: ../discussion/form-vs-fetcher
[form]: ../components/form
[fetcher-submit]: ../hooks/use-fetcher#fetchersubmitformdata-options
[flush-sync]: https://react.dev/reference/react-dom/flushSync
[start-transition]: https://react.dev/reference/react/startTransition
[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
[use-view-transition-state]: ../hooks//use-view-transition-state
4 changes: 2 additions & 2 deletions integration/form-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@ test.describe("Forms", () => {
await app.goto("/projects/blarg");
let html = await app.getHtml();
let el = getElement(html, `#${SPLAT_ROUTE_NO_ACTION}`);
expect(el.attr("action")).toMatch("/projects");
expect(el.attr("action")).toMatch("/projects/blarg");
});

test("no action resolves to URL including search params", async ({
Expand All @@ -931,7 +931,7 @@ test.describe("Forms", () => {
await app.goto("/projects/blarg?foo=bar");
let html = await app.getHtml();
let el = getElement(html, `#${SPLAT_ROUTE_NO_ACTION}`);
expect(el.attr("action")).toMatch("/projects?foo=bar");
expect(el.attr("action")).toMatch("/projects/blarg?foo=bar");
});

test("absolute action resolves relative to the root route", async ({
Expand Down
Loading

0 comments on commit d475df4

Please sign in to comment.