|
1 | 1 | # `react-router-dom-v5-compat`
|
2 | 2 |
|
| 3 | +## 6.21.0-pre.0 |
| 4 | + |
| 5 | +### Minor Changes |
| 6 | + |
| 7 | +- Add a new `future.v7_relativeSplatPath` flag to implenent a breaking bug fix to relative routing when inside a splat route. ([#11087](https://github.com/remix-run/react-router/pull/11087)) |
| 8 | + |
| 9 | + This fix was originally added in [#10983](https://github.com/remix-run/react-router/issues/10983) and was later reverted in [#11078](https://github.com/remix-run/react-router/issues/110788) because it was determined that a large number of existing applications were relying on the buggy behavior (see [#11052](https://github.com/remix-run/react-router/issues/11052)) |
| 10 | + |
| 11 | + **The Bug** |
| 12 | + The buggy behavior is that without this flag, the default behavior when resolving relative paths is to _ignore_ any splat (`*`) portion of the current route path. |
| 13 | + |
| 14 | + **The Background** |
| 15 | + This decision was originally made thinking that it would make the concept of nested different sections of your apps in `<Routes>` easier if relative routing would _replace_ the current splat: |
| 16 | + |
| 17 | + ```jsx |
| 18 | + <BrowserRouter> |
| 19 | + <Routes> |
| 20 | + <Route path="/" element={<Home />} /> |
| 21 | + <Route path="dashboard/*" element={<Dashboard />} /> |
| 22 | + </Routes> |
| 23 | + </BrowserRouter> |
| 24 | + ``` |
| 25 | + |
| 26 | + Any paths like `/dashboard`, `/dashboard/team`, `/dashboard/projects` will match the `Dashboard` route. The dashboard component itself can then render nested `<Routes>`: |
| 27 | + |
| 28 | + ```jsx |
| 29 | + function Dashboard() { |
| 30 | + return ( |
| 31 | + <div> |
| 32 | + <h2>Dashboard</h2> |
| 33 | + <nav> |
| 34 | + <Link to="/">Dashboard Home</Link> |
| 35 | + <Link to="team">Team</Link> |
| 36 | + <Link to="projects">Projects</Link> |
| 37 | + </nav> |
| 38 | + |
| 39 | + <Routes> |
| 40 | + <Route path="/" element={<DashboardHome />} /> |
| 41 | + <Route path="team" element={<DashboardTeam />} /> |
| 42 | + <Route path="projects" element={<DashboardProjects />} /> |
| 43 | + </Routes> |
| 44 | + </div> |
| 45 | + ); |
| 46 | + } |
| 47 | + ``` |
| 48 | + |
| 49 | + Now, all links and route paths are relative to the router above them. This makes code splitting and compartmentalizing your app really easy. You could render the `Dashboard` as its own independent app, or embed it into your large app without making any changes to it. |
| 50 | + |
| 51 | + **The Problem** |
| 52 | + |
| 53 | + The problem is that this concept of ignoring part of a pth breaks a lot of other assumptions in React Router - namely that `"."` always means the current location pathname for that route. When we ignore the splat portion, we start getting invalid paths when using `"."`: |
| 54 | + |
| 55 | + ```jsx |
| 56 | + // If we are on URL /dashboard/team, and we want to link to /dashboard/team: |
| 57 | + function DashboardTeam() { |
| 58 | + // ❌ This is broken and results in <a href="/dashboard"> |
| 59 | + return <Link to=".">A broken link to the Current URL</Link>; |
| 60 | + |
| 61 | + // ✅ This is fixed but super unintuitive since we're already at /dashboard/team! |
| 62 | + return <Link to="./team">A broken link to the Current URL</Link>; |
| 63 | + } |
| 64 | + ``` |
| 65 | + |
| 66 | + We've also introduced an issue that we can no longer move our `DashboardTeam` component around our route hierarchy easily - since it behaves differently if we're underneath a non-splat route, such as `/dashboard/:widget`. Now, our `"."` links will, properly point to ourself _inclusive of the dynamic param value_ so behavior will break from it's corresponding usage in a `/dashboard/*` route. |
| 67 | + |
| 68 | + Even worse, consider a nested splat route configuration: |
| 69 | + |
| 70 | + ```jsx |
| 71 | + <BrowserRouter> |
| 72 | + <Routes> |
| 73 | + <Route path="dashboard"> |
| 74 | + <Route path="*" element={<Dashboard />} /> |
| 75 | + </Route> |
| 76 | + </Routes> |
| 77 | + </BrowserRouter> |
| 78 | + ``` |
| 79 | + |
| 80 | + Now, a `<Link to=".">` and a `<Link to="..">` inside the `Dashboard` component go to the same place! That is definitely not correct! |
| 81 | + |
| 82 | + Another common issue arose in Data Routers (and Remix) where any `<Form>` should post to it's own route `action` if you the user doesn't specify a form action: |
| 83 | + |
| 84 | + ```jsx |
| 85 | + let router = createBrowserRouter({ |
| 86 | + path: "/dashboard", |
| 87 | + children: [ |
| 88 | + { |
| 89 | + path: "*", |
| 90 | + action: dashboardAction, |
| 91 | + Component() { |
| 92 | + // ❌ This form is broken! It throws a 405 error when it submits because |
| 93 | + // it tries to submit to /dashboard (without the splat value) and the parent |
| 94 | + // `/dashboard` route doesn't have an action |
| 95 | + return <Form method="post">...</Form>; |
| 96 | + }, |
| 97 | + }, |
| 98 | + ], |
| 99 | + }); |
| 100 | + ``` |
| 101 | + |
| 102 | + This is just a compounded issue from the above because the default location for a `Form` to submit to is itself (`"."`) - and if we ignore the splat portion, that now resolves to the parent route. |
| 103 | + |
| 104 | + **The Solution** |
| 105 | + If you are leveraging this behavior, it's recommended to enable the future flag, move your splat to it's own route, and leverage `../` for any links to "sibling" pages: |
| 106 | + |
| 107 | + ```jsx |
| 108 | + <BrowserRouter> |
| 109 | + <Routes> |
| 110 | + <Route path="dashboard"> |
| 111 | + <Route path="*" element={<Dashboard />} /> |
| 112 | + </Route> |
| 113 | + </Routes> |
| 114 | + </BrowserRouter> |
| 115 | + |
| 116 | + function Dashboard() { |
| 117 | + return ( |
| 118 | + <div> |
| 119 | + <h2>Dashboard</h2> |
| 120 | + <nav> |
| 121 | + <Link to="..">Dashboard Home</Link> |
| 122 | + <Link to="../team">Team</Link> |
| 123 | + <Link to="../projects">Projects</Link> |
| 124 | + </nav> |
| 125 | + |
| 126 | + <Routes> |
| 127 | + <Route path="/" element={<DashboardHome />} /> |
| 128 | + <Route path="team" element={<DashboardTeam />} /> |
| 129 | + <Route path="projects" element={<DashboardProjects />} /> |
| 130 | + </Router> |
| 131 | + </div> |
| 132 | + ); |
| 133 | + } |
| 134 | + ``` |
| 135 | + |
| 136 | + This way, `.` means "the full current pathname for my route" in all cases (including static, dynamic, and splat routes) and `..` always means "my parents pathname". |
| 137 | + |
| 138 | +### Patch Changes |
| 139 | + |
| 140 | +- Updated dependencies: |
| 141 | + - `react-router@6.21.0-pre.0` |
| 142 | + - `react-router-dom@6.21.0-pre.0` |
| 143 | + |
3 | 144 | ## 6.20.1
|
4 | 145 |
|
5 | 146 | ### Patch Changes
|
|
0 commit comments