Skip to content

Commit e6b6811

Browse files
Fix: useResolvedPath and useNavigate use different logic (#8985)
* share matches logic between useResolvedPath and useNavigate * sign CLA * Fix 2 tests and add some comments * add changeset * update test descriptions for clarity * change back the ../../.. to ../.. Co-authored-by: Matt Brophy <[email protected]>
1 parent 2f9b155 commit e6b6811

File tree

4 files changed

+42
-14
lines changed

4 files changed

+42
-14
lines changed

.changeset/silver-planes-relate.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: Additional logic fixed for relative navigation from index/pathless layout routes (#8985)

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- chrisngobanh
1414
- christopherchudzicki
1515
- cvbuelow
16+
- david-crespo
1617
- edwin177
1718
- elylucas
1819
- emzoumpo

packages/react-router-dom/__tests__/link-href-test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ describe("<Link> href", () => {
179179
expect(renderer.root.findByType("a").props.href).toEqual("/inbox");
180180
});
181181

182-
test('<Link to=".."> resolves relative to the parent route', () => {
182+
test('<Link to=".."> resolves relative to the parent route (ignoring the index route)', () => {
183183
let renderer: TestRenderer.ReactTestRenderer;
184184
TestRenderer.act(() => {
185185
renderer = TestRenderer.create(
@@ -193,7 +193,7 @@ describe("<Link> href", () => {
193193
);
194194
});
195195

196-
expect(renderer.root.findByType("a").props.href).toEqual("/inbox");
196+
expect(renderer.root.findByType("a").props.href).toEqual("/");
197197
});
198198

199199
test('<Link to=".."> with more .. segments than parent routes resolves to the root URL', () => {
@@ -262,7 +262,7 @@ describe("<Link> href", () => {
262262
expect(renderer.root.findByType("a").props.href).toEqual("/inbox");
263263
});
264264

265-
test('<Link to=".."> resolves relative to the parent route', () => {
265+
test('<Link to=".."> resolves relative to the parent route (ignoring the pathless route)', () => {
266266
let renderer: TestRenderer.ReactTestRenderer;
267267
TestRenderer.act(() => {
268268
renderer = TestRenderer.create(
@@ -278,7 +278,7 @@ describe("<Link> href", () => {
278278
);
279279
});
280280

281-
expect(renderer.root.findByType("a").props.href).toEqual("/inbox");
281+
expect(renderer.root.findByType("a").props.href).toEqual("/");
282282
});
283283

284284
test('<Link to=".."> with more .. segments than parent routes resolves to the root URL', () => {

packages/react-router/lib/hooks.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,36 @@ export interface NavigateFunction {
137137
(delta: number): void;
138138
}
139139

140+
/**
141+
* When processing relative navigation we want to ignore ancestor routes that
142+
* do not contribute to the path, such that index/pathless layout routes don't
143+
* interfere.
144+
*
145+
* For example, when moving a route element into an index route and/or a
146+
* pathless layout route, relative link behavior contained within should stay
147+
* the same. Both of the following examples should link back to the root:
148+
*
149+
* <Route path="/">
150+
* <Route path="accounts" element={<Link to=".."}>
151+
* </Route>
152+
*
153+
* <Route path="/">
154+
* <Route path="accounts">
155+
* <Route element={<AccountsLayout />}> // <-- Does not contribute
156+
* <Route index element={<Link to=".."} /> // <-- Does not contribute
157+
* </Route
158+
* </Route>
159+
* </Route>
160+
*/
161+
function getPathContributingMatches(matches: RouteMatch[]) {
162+
return matches.filter(
163+
(match, index) =>
164+
index === 0 ||
165+
(!match.route.index &&
166+
match.pathnameBase !== matches[index - 1].pathnameBase)
167+
);
168+
}
169+
140170
/**
141171
* Returns an imperative method for changing the location. Used by <Link>s, but
142172
* may also be used by other elements to change the location.
@@ -155,16 +185,8 @@ export function useNavigate(): NavigateFunction {
155185
let { matches } = React.useContext(RouteContext);
156186
let { pathname: locationPathname } = useLocation();
157187

158-
// Ignore index + pathless matches
159-
let pathContributingMatches = matches.filter(
160-
(match, index) =>
161-
index === 0 ||
162-
(!match.route.index &&
163-
match.pathnameBase !== matches[index - 1].pathnameBase)
164-
);
165-
166188
let routePathnamesJson = JSON.stringify(
167-
pathContributingMatches.map((match) => match.pathnameBase)
189+
getPathContributingMatches(matches).map((match) => match.pathnameBase)
168190
);
169191

170192
let activeRef = React.useRef(false);
@@ -262,7 +284,7 @@ export function useResolvedPath(to: To): Path {
262284
let { pathname: locationPathname } = useLocation();
263285

264286
let routePathnamesJson = JSON.stringify(
265-
matches.map((match) => match.pathnameBase)
287+
getPathContributingMatches(matches).map((match) => match.pathnameBase)
266288
);
267289

268290
return React.useMemo(

0 commit comments

Comments
 (0)