Skip to content

Commit cb930b9

Browse files
committed
Merge branch 'dev' into brophdawg11/spa-middleware-hydration
2 parents 524aed1 + e04ad2b commit cb930b9

Some content is hidden

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

41 files changed

+6530
-3904
lines changed

.changeset/long-brooms-shake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
[UNSTABLE] Add `unstable_pattern` to the parameters for client side `unstable_onError`, refactor how it's called by `RouterProvider` to avoid potential strict mode issues

.changeset/olive-planets-think.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Add new `unstable_useTransitions` flag to routers to give users control over the usage of [`React.startTransition`](https://react.dev/reference/react/startTransition) and [`React.useOptimistic`](https://react.dev/reference/react/useOptimistic).
6+
7+
- Framework Mode + Data Mode:
8+
- `<HydratedRouter unstable_transition>`/`<RouterProvider unstable_transition>`
9+
- When left unset (current default behavior)
10+
- Router state updates are wrapped in `React.startTransition`
11+
- ⚠️ This can lead to buggy behaviors if you are wrapping your own navigations/fetchers in `React.startTransition`
12+
- You should set the flag to `true` if you run into this scenario to get the enhanced `useOptimistic` behavior (requires React 19)
13+
- When set to `true`
14+
- Router state updates remain wrapped in `React.startTransition` (as they are without the flag)
15+
- `Link`/`Form` navigations will be wrapped in `React.startTransition`
16+
- A subset of router state info will be surfaced to the UI _during_ navigations via `React.useOptimistic` (i.e., `useNavigation()`, `useFetchers()`, etc.)
17+
- ⚠️ This is a React 19 API so you must also be React 19 to opt into this flag for Framework/Data Mode
18+
- When set to `false`
19+
- The router will not leverage `React.startTransition` or `React.useOptimistic` on any navigations or state changes
20+
- Declarative Mode
21+
- `<BrowserRouter unstable_useTransitions>`
22+
- When left unset
23+
- Router state updates are wrapped in `React.startTransition`
24+
- When set to `true`
25+
- Router state updates remain wrapped in `React.startTransition` (as they are without the flag)
26+
- `Link`/`Form` navigations will be wrapped in `React.startTransition`
27+
- When set to `false`
28+
- the router will not leverage `React.startTransition` on any navigations or state changes

.changeset/real-chairs-exercise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix the promise returned from `useNavigate` in Framework/Data Mode so that it properly tracks the duration of `popstate` navigations (i.e., `navigate(-1)`)

.changeset/rotten-grapes-cough.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"react-router": minor
3+
---
4+
5+
Stabilize `fetcher.reset()`
6+
7+
- ⚠️ This is a breaking change if you have begun using `fetcher.unstable_reset()`

.changeset/shiny-pans-admire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix internal type error in useRoute types that surfaces when skipLibCheck is disabled

.changeset/spicy-camels-love.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Optimize href() to avoid backtracking regex on splat

docs/api/data-routers/RouterProvider.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ function RouterProvider({
4949
router,
5050
flushSync: reactDomFlushSyncImpl,
5151
unstable_onError,
52+
unstable_useTransitions,
5253
}: RouterProviderProps): React.ReactElement
5354
```
5455

@@ -86,3 +87,22 @@ and is only present for render errors.
8687

8788
The [`DataRouter`](https://api.reactrouter.com/v7/interfaces/react_router.DataRouter.html) instance to use for navigation and data fetching.
8889

90+
### unstable_useTransitions
91+
92+
Control whether router state updates are internally wrapped in
93+
[`React.startTransition`](https://react.dev/reference/react/startTransition).
94+
95+
- When left `undefined`, all state updates are wrapped in
96+
`React.startTransition`
97+
- This can lead to buggy behaviors if you are wrapping your own
98+
navigations/fetchers in `startTransition`.
99+
- When set to `true`, [`Link`](../components/Link) and [`Form`](../components/Form) navigations will be wrapped
100+
in `React.startTransition` and router state changes will be wrapped in
101+
`React.startTransition` and also sent through
102+
[`useOptimistic`](https://react.dev/reference/react/useOptimistic) to
103+
surface mid-navigation router state changes to the UI.
104+
- When set to `false`, the router will not leverage `React.startTransition` or
105+
`React.useOptimistic` on any navigations or state changes.
106+
107+
For more information, please see the [docs](https://reactrouter.com/explanation/react-transitions).
108+

docs/api/data-routers/createBrowserRouter.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,55 @@ const router = createBrowserRouter(
329329
);
330330
```
331331

332+
### opts.unstable_instrumentations
333+
334+
Array of instrumentation objects allowing you to instrument the router and
335+
individual routes prior to router initialization (and on any subsequently
336+
added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
337+
mostly useful for observability such as wrapping navigations, fetches,
338+
as well as route loaders/actions/middlewares with logging and/or performance
339+
tracing.
340+
341+
```tsx
342+
let router = createBrowserRouter(routes, {
343+
unstable_instrumentations: [logging]
344+
});
345+
346+
347+
let logging = {
348+
router({ instrument }) {
349+
instrument({
350+
navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
351+
fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
352+
});
353+
},
354+
route({ instrument, id }) {
355+
instrument({
356+
middleware: (impl, info) => logExecution(
357+
`middleware ${info.request.url} (route ${id})`,
358+
impl
359+
),
360+
loader: (impl, info) => logExecution(
361+
`loader ${info.request.url} (route ${id})`,
362+
impl
363+
),
364+
action: (impl, info) => logExecution(
365+
`action ${info.request.url} (route ${id})`,
366+
impl
367+
),
368+
})
369+
}
370+
};
371+
372+
async function logExecution(label: string, impl: () => Promise<void>) {
373+
let start = performance.now();
374+
console.log(`start ${label}`);
375+
await impl();
376+
let duration = Math.round(performance.now() - start);
377+
console.log(`end ${label} (${duration}ms)`);
378+
}
379+
```
380+
332381
### opts.patchRoutesOnNavigation
333382

334383
Lazily define portions of the route tree on navigations.

docs/api/data-routers/createHashRouter.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,55 @@ const router = createBrowserRouter(
142142
);
143143
```
144144

145+
### opts.unstable_instrumentations
146+
147+
Array of instrumentation objects allowing you to instrument the router and
148+
individual routes prior to router initialization (and on any subsequently
149+
added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
150+
mostly useful for observability such as wrapping navigations, fetches,
151+
as well as route loaders/actions/middlewares with logging and/or performance
152+
tracing.
153+
154+
```tsx
155+
let router = createBrowserRouter(routes, {
156+
unstable_instrumentations: [logging]
157+
});
158+
159+
160+
let logging = {
161+
router({ instrument }) {
162+
instrument({
163+
navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
164+
fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
165+
});
166+
},
167+
route({ instrument, id }) {
168+
instrument({
169+
middleware: (impl, info) => logExecution(
170+
`middleware ${info.request.url} (route ${id})`,
171+
impl
172+
),
173+
loader: (impl, info) => logExecution(
174+
`loader ${info.request.url} (route ${id})`,
175+
impl
176+
),
177+
action: (impl, info) => logExecution(
178+
`action ${info.request.url} (route ${id})`,
179+
impl
180+
),
181+
})
182+
}
183+
};
184+
185+
async function logExecution(label: string, impl: () => Promise<void>) {
186+
let start = performance.now();
187+
console.log(`start ${label}`);
188+
await impl();
189+
let duration = Math.round(performance.now() - start);
190+
console.log(`end ${label} (${duration}ms)`);
191+
}
192+
```
193+
145194
### opts.dataStrategy
146195

147196
Override the default data strategy of running loaders in parallel.

docs/api/data-routers/createMemoryRouter.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,55 @@ Initial entries in the in-memory history stack
7575

7676
Index of `initialEntries` the application should initialize to
7777

78+
### opts.unstable_instrumentations
79+
80+
Array of instrumentation objects allowing you to instrument the router and
81+
individual routes prior to router initialization (and on any subsequently
82+
added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
83+
mostly useful for observability such as wrapping navigations, fetches,
84+
as well as route loaders/actions/middlewares with logging and/or performance
85+
tracing.
86+
87+
```tsx
88+
let router = createBrowserRouter(routes, {
89+
unstable_instrumentations: [logging]
90+
});
91+
92+
93+
let logging = {
94+
router({ instrument }) {
95+
instrument({
96+
navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
97+
fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
98+
});
99+
},
100+
route({ instrument, id }) {
101+
instrument({
102+
middleware: (impl, info) => logExecution(
103+
`middleware ${info.request.url} (route ${id})`,
104+
impl
105+
),
106+
loader: (impl, info) => logExecution(
107+
`loader ${info.request.url} (route ${id})`,
108+
impl
109+
),
110+
action: (impl, info) => logExecution(
111+
`action ${info.request.url} (route ${id})`,
112+
impl
113+
),
114+
})
115+
}
116+
};
117+
118+
async function logExecution(label: string, impl: () => Promise<void>) {
119+
let start = performance.now();
120+
console.log(`start ${label}`);
121+
await impl();
122+
let duration = Math.round(performance.now() - start);
123+
console.log(`end ${label} (${duration}ms)`);
124+
}
125+
```
126+
78127
### opts.patchRoutesOnNavigation
79128

80129
Lazily define portions of the route tree on navigations.

0 commit comments

Comments
 (0)