-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: useResolvedPath
and useNavigate
use different logic
#8985
Conversation
🦋 Changeset detectedLatest commit: e70cf75 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Hi @david-crespo, Welcome, and thank you for contributing to React Router! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at [email protected]. Thanks! - The Remix team |
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
useResolvedPath
and useNavigate
useResolvedPath
and useNavigate
use different logic
I see the failing tests, taking a look. Seems like maybe the |
I noticed that in this test (which this PR causes to fail), the description does not match what is asserted. This PR makes it the root URL and not react-router/packages/react-router-dom/__tests__/link-href-test.tsx Lines 54 to 70 in 546d778
|
@@ -58,7 +58,7 @@ describe("<Link> href", () => { | |||
<MemoryRouter initialEntries={["/inbox/messages"]}> | |||
<Routes> | |||
<Route path="inbox"> | |||
<Route path="messages" element={<Link to="../../about" />} /> | |||
<Route path="messages" element={<Link to="../../../about" />} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test was incorrect before - ../../about
would be the correct # of ..
's. The test passed but it wasn't asserting having more segments than parent routes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this link going completely out of the routes? ..
traverses the route hierarchy, so:
- the route hierarchy at
/inbox/messages
is[inbox, messages]
..
goes to[inbox]
../..
goes to[]
../../..
goes completely out of everything
can you help me understand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the description, I thought that was the point of this test — showing that even if you accidentally put too many ..
you’ll still get /
instead of an error or something nonsensical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that was my understanding as well from the test description, proving that links are "safe" insofar that you can't goof yourself by doing too many
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this test and the one above in the original commit they were added, it seems clear this is just about whether there are more ..
than parent Route
components. The first test shows that ..
takes you to the parent Route
, and the second shows that you can resolve ../../
to /
even when there is no explicit <Route path="/">
. So it does seem like ../../about
is enough to satisfy the goal of the test. I changed them back.
It could be worth changing more .. segments than parent routes
to more .. segments than parent <Route>s
or something to avoid this confusion.
react-router/packages/react-router-dom/__tests__/link-href-test.tsx
Lines 31 to 58 in 15cdd0d
test('<Link to=".."> resolves relative to the parent route', () => { | |
let renderer = createTestRenderer( | |
<MemoryRouter initialEntries={["/inbox/messages"]}> | |
<Routes> | |
<Route path="inbox"> | |
<Route path="messages" element={<Link to=".." />} /> | |
</Route> | |
</Routes> | |
</MemoryRouter> | |
); | |
expect(renderer.root.findByType("a").props.href).toEqual("/inbox"); | |
}); | |
test('<Link to=".."> with more .. segments than parent routes resolves to the root URL', () => { | |
let renderer = createTestRenderer( | |
<MemoryRouter initialEntries={["/inbox/messages"]}> | |
<Routes> | |
<Route path="inbox"> | |
<Route path="messages" element={<Link to="../../about" />} /> | |
</Route> | |
</Routes> | |
</MemoryRouter> | |
); | |
expect(renderer.root.findByType("a").props.href).toEqual("/about"); | |
}); | |
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine if we change them back 👍 . Michael's on vacation this week so we won't be able to get any clarity on exactly what he may have envisioned as the purpose for the tests so probably best to leave them unchanged. My main concern was that we weren't properly asserting the following line in resolveTo
:
// If there are more ".." segments than parent routes, resolve relative to
// the root / URL.
from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
But - funny enough, even if we change that to from = routePathnames[routePathnameIndex]
everything still works since from
being undefined
causes it to default to "/"
in resolvePath
anyway so we still get the same behavior.
As to the original commit intent - I think it makes sense that ../../about
still passes - I would argue that it's not asserting the "more than" aspect of the description. ../../about
should go back to /about
because its correctly traversing the exact number of parents that it has. Adding the third ..
tests the scenario that you use "more than". I altered the test locally to show how I'm thinking about it:
test('<Link to=".."> with more .. segments than parent routes resolves to the root URL', () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(
<MemoryRouter initialEntries={["/inbox/messages"]}>
<Routes>
<Route path="inbox">
<Route
path="messages"
element={
<>
<Link to="./about" /> // 1
<Link to="../about" /> // 2
<Link to="../../about" /> // 3
<Link to="../../../about" /> // 4
</>
}
/>
</Route>
</Routes>
</MemoryRouter>
);
});
expect(renderer.root.findAllByType("a").map((a) => a.props.href)).toEqual([
"/inbox/messages/about",
"/inbox/about",
"/about",
"/about",
]);
});
- Link 2 and 3 assert the same thing - that we can traverse upwards with
..
- Link 4 asserts something different - specifically when we have "more than"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do like the clarity of having all 4!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, I reviewed too quickly, the test says "more than parents" so I like this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
K, I’ll revert my revert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spoke to Ryan - going to merge this as-is - these existing tests are a bit tangentially related so I'll re-make the changes in another PR 👍
@@ -132,7 +132,7 @@ describe("<Link> href", () => { | |||
<Route path="inbox"> | |||
<Route | |||
path="messages/:id" | |||
element={<Link to="../../about" />} | |||
element={<Link to="../../../about" />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
@@ -193,7 +193,7 @@ describe("<Link> href", () => { | |||
); | |||
}); | |||
|
|||
expect(renderer.root.findByType("a").props.href).toEqual("/inbox"); | |||
expect(renderer.root.findByType("a").props.href).toEqual("/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ..
should ignore the index
route and thus be relative to inbox
which would resolve to the root. The incorrect nature of this test can be seen by comparing to the test above where in this same layout, <Link to=".">
resolves to /inbox
- so ..
should resolve to /
@@ -278,7 +278,7 @@ describe("<Link> href", () => { | |||
); | |||
}); | |||
|
|||
expect(renderer.root.findByType("a").props.href).toEqual("/inbox"); | |||
expect(renderer.root.findByType("a").props.href).toEqual("/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
Thanks for the PR @david-crespo! I agree some of those existing tests were incorrect given the bug fix this is addressing 👍 . Going to kick this over to @ryanflorence for a final approval but this should be good to go. |
let routePathnamesJson = JSON.stringify( | ||
pathContributingMatches.map((match) => match.pathnameBase) | ||
getPathContributingMatches(matches).map((match) => match.pathnameBase) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a feeling doing the JSON.stringify
inline in both spots was where it would end up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it certainly worked fine, just felt a little distanced in the shared util since it's a dependency implementation detail for the individual hooks
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Discussed in Discord (thread). #8697 and #8954 updated the logic in
useNavigate
without making the corresponding change touseResolvedPath
, which is used to set thehref
onLink
s. This means there are cases where the href is correct but the navigate behavior is wrong and vice versa.Going to try to add a test that reproduces the bug I was seeing.