Skip to content

Commit

Permalink
Fix usage of Component API within descendant routes (#10434)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed May 2, 2023
1 parent fdb9069 commit 666d962
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/descendant-routes-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-router": patch
---

Fix usage of `Component` API within descendant `<Routes>`
4 changes: 3 additions & 1 deletion docs/route/route.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const router = createBrowserRouter(

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.

<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>
<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>

## Type declaration

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

<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>

## `errorElement`/`ErrorBoundary`

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`.
Expand Down
19 changes: 19 additions & 0 deletions packages/react-router/__tests__/Route-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ describe("A <Route>", () => {
`);
});

it("renders its `Component` prop", () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(
<MemoryRouter initialEntries={["/home"]}>
<Routes>
<Route path="home" Component={() => <h1>Home</h1>} />
</Routes>
</MemoryRouter>
);
});

expect(renderer.toJSON()).toMatchInlineSnapshot(`
<h1>
Home
</h1>
`);
});

it("renders its child routes when no `element` prop is given", () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
Expand Down
8 changes: 8 additions & 0 deletions packages/react-router/lib/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,14 @@ export function _renderMatches(
let children: React.ReactNode;
if (error) {
children = errorElement;
} else if (match.route.Component) {
// Note: This is a de-optimized path since React won't re-use the
// ReactElement since it's identity changes with each new
// React.createElement call. We keep this so folks can use
// `<Route Component={...}>` in `<Routes>` but generally `Component`
// usage is only advised in `RouterProvider` when we can convert it to
// `element` ahead of time.
children = <match.route.Component />;
} else if (match.route.element) {
children = match.route.element;
} else {
Expand Down

0 comments on commit 666d962

Please sign in to comment.