Skip to content

Commit 666d962

Browse files
authored
Fix usage of Component API within descendant routes (#10434)
1 parent fdb9069 commit 666d962

File tree

4 files changed

+35
-1
lines changed

4 files changed

+35
-1
lines changed
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 usage of `Component` API within descendant `<Routes>`

docs/route/route.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const router = createBrowserRouter(
6262

6363
Neither style is discouraged and behavior is identical. For the majority of this doc we will use the JSX style because that's what most people are accustomed to in the context of React Router.
6464

65-
<docs-info>If you do not wish to specify a React element (i.e., `element={<MyComponent />}`) you may specify a `Component` instead (i.e., `Component={MyComponent}`) and React Router will call `createElement` for you internally.</docs-info>
65+
<docs-info>When using `RouterProvider`, if you do not wish to specify a React element (i.e., `element={<MyComponent />}`) you may specify a `Component` instead (i.e., `Component={MyComponent}`) and React Router will call `createElement` for you internally. You should only do this for `RouterProvider` applications though since using `Component` inside of `<Routes>` will de-optimize React's ability to reuse the created element across renders.</docs-info>
6666

6767
## Type declaration
6868

@@ -312,6 +312,8 @@ Otherwise use `Component` and React Router will create the React Element for you
312312
<Route path="/for-sale" Component={Properties} />
313313
```
314314

315+
<docs-warning>You should only opt into the `Component` API for data routes via `RouterProvider`. Using this API on a `<Route>` inside `<Routes>` will de-optimize React's ability to reuse the created element across renders.</docs-warning>
316+
315317
## `errorElement`/`ErrorBoundary`
316318

317319
When a route throws an exception while rendering, in a `loader` or in an `action`, this React Element/Component will render instead of the normal `element`/`Component`.

packages/react-router/__tests__/Route-test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@ describe("A <Route>", () => {
2222
`);
2323
});
2424

25+
it("renders its `Component` prop", () => {
26+
let renderer: TestRenderer.ReactTestRenderer;
27+
TestRenderer.act(() => {
28+
renderer = TestRenderer.create(
29+
<MemoryRouter initialEntries={["/home"]}>
30+
<Routes>
31+
<Route path="home" Component={() => <h1>Home</h1>} />
32+
</Routes>
33+
</MemoryRouter>
34+
);
35+
});
36+
37+
expect(renderer.toJSON()).toMatchInlineSnapshot(`
38+
<h1>
39+
Home
40+
</h1>
41+
`);
42+
});
43+
2544
it("renders its child routes when no `element` prop is given", () => {
2645
let renderer: TestRenderer.ReactTestRenderer;
2746
TestRenderer.act(() => {

packages/react-router/lib/hooks.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,14 @@ export function _renderMatches(
689689
let children: React.ReactNode;
690690
if (error) {
691691
children = errorElement;
692+
} else if (match.route.Component) {
693+
// Note: This is a de-optimized path since React won't re-use the
694+
// ReactElement since it's identity changes with each new
695+
// React.createElement call. We keep this so folks can use
696+
// `<Route Component={...}>` in `<Routes>` but generally `Component`
697+
// usage is only advised in `RouterProvider` when we can convert it to
698+
// `element` ahead of time.
699+
children = <match.route.Component />;
692700
} else if (match.route.element) {
693701
children = match.route.element;
694702
} else {

0 commit comments

Comments
 (0)