Skip to content

Commit b8af575

Browse files
committed
enhance relative navigation from determination
1 parent 72b65cf commit b8af575

File tree

3 files changed

+72
-43
lines changed

3 files changed

+72
-43
lines changed

packages/react-router/src/link.tsx

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,29 @@ export function useLinkProps<
9999
structuralSharing: true as any,
100100
})
101101

102-
const from = useMatch({
102+
const matchIndex = useMatch({
103103
strict: false,
104-
select: (match) => options.from ?? match.fullPath,
104+
select: (match) => match.index,
105105
})
106106

107+
const getFrom = React.useCallback( () => {
108+
const currentRouteMatches= router.matchRoutes(router.latestLocation, {
109+
_buildLocation: false,
110+
})
111+
112+
return options.from ??
113+
currentRouteMatches.slice(-1)[0]?.fullPath ??
114+
router.state.matches[matchIndex]!.fullPath
115+
}, [router, options.from, matchIndex])
116+
107117
const next = React.useMemo(
108-
() => router.buildLocation({ ...options, from } as any),
118+
() => router.buildLocation({ ...options, from: getFrom() } as any),
109119
// eslint-disable-next-line react-hooks/exhaustive-deps
110120
[
111121
router,
112122
currentSearch,
113123
options._fromLocation,
114-
from,
124+
options.from,
115125
options.hash,
116126
options.to,
117127
options.search,
@@ -182,7 +192,7 @@ export function useLinkProps<
182192

183193
const doPreload = React.useCallback(
184194
() => {
185-
router.preloadRoute({ ...options, from } as any).catch((err) => {
195+
router.preloadRoute({ ...options, from: getFrom() } as any).catch((err) => {
186196
console.warn(err)
187197
console.warn(preloadWarning)
188198
})
@@ -192,7 +202,7 @@ export function useLinkProps<
192202
router,
193203
options.to,
194204
options._fromLocation,
195-
from,
205+
options.from,
196206
options.search,
197207
options.hash,
198208
options.params,
@@ -235,24 +245,18 @@ export function useLinkProps<
235245
}
236246
}, [disabled, doPreload, preload])
237247

238-
if (isExternal) {
239-
return {
240-
...propsSafeToSpread,
241-
ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
242-
type,
243-
href: to,
244-
...(children && { children }),
245-
...(target && { target }),
246-
...(disabled && { disabled }),
247-
...(style && { style }),
248-
...(className && { className }),
249-
...(onClick && { onClick }),
250-
...(onFocus && { onFocus }),
251-
...(onMouseEnter && { onMouseEnter }),
252-
...(onMouseLeave && { onMouseLeave }),
253-
...(onTouchStart && { onTouchStart }),
254-
}
255-
}
248+
const navigate = React.useCallback(() => {
249+
router.navigate({
250+
...options,
251+
from: getFrom(),
252+
replace,
253+
resetScroll,
254+
hashScrollIntoView,
255+
startTransition,
256+
viewTransition,
257+
ignoreBlocker,
258+
})
259+
}, [router, options, getFrom, replace, resetScroll, hashScrollIntoView, startTransition, viewTransition, ignoreBlocker])
256260

257261
// The click handler
258262
const handleClick = (e: React.MouseEvent) => {
@@ -276,16 +280,26 @@ export function useLinkProps<
276280

277281
// All is well? Navigate!
278282
// N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing
279-
router.navigate({
280-
...options,
281-
from,
282-
replace,
283-
resetScroll,
284-
hashScrollIntoView,
285-
startTransition,
286-
viewTransition,
287-
ignoreBlocker,
288-
})
283+
navigate()
284+
}
285+
}
286+
287+
if (isExternal) {
288+
return {
289+
...propsSafeToSpread,
290+
ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
291+
type,
292+
href: to,
293+
...(children && { children }),
294+
...(target && { target }),
295+
...(disabled && { disabled }),
296+
...(style && { style }),
297+
...(className && { className }),
298+
...(onClick && { onClick }),
299+
...(onFocus && { onFocus }),
300+
...(onMouseEnter && { onMouseEnter }),
301+
...(onMouseLeave && { onMouseLeave }),
302+
...(onTouchStart && { onTouchStart }),
289303
}
290304
}
291305

packages/react-router/src/useNavigate.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function useNavigate<
1515
>(_defaultOpts?: {
1616
from?: FromPathOption<TRouter, TDefaultFrom>
1717
}): UseNavigateResult<TDefaultFrom> {
18-
const { navigate, state } = useRouter()
18+
const router = useRouter()
1919

2020
// Just get the index of the current match to avoid rerenders
2121
// as much as possible
@@ -26,18 +26,23 @@ export function useNavigate<
2626

2727
return React.useCallback(
2828
(options: NavigateOptions) => {
29+
const currentRouteMatches= router.matchRoutes(router.latestLocation, {
30+
_buildLocation: false,
31+
})
32+
2933
const from =
3034
options.from ??
3135
_defaultOpts?.from ??
32-
state.matches[matchIndex]!.fullPath
36+
currentRouteMatches.slice(-1)[0]?.fullPath ??
37+
router.state.matches[matchIndex]!.fullPath
3338

34-
return navigate({
39+
return router.navigate({
3540
...options,
3641
from,
3742
})
3843
},
3944
// eslint-disable-next-line react-hooks/exhaustive-deps
40-
[_defaultOpts?.from, navigate],
45+
[_defaultOpts?.from, router.navigate, router.latestLocation],
4146
) as UseNavigateResult<TDefaultFrom>
4247
}
4348

packages/router-core/src/router.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,14 +1419,24 @@ export class RouterCore<
14191419
// First let's find the starting pathname
14201420
// By default, start with the current location
14211421
let fromPath = this.resolvePathWithBase(lastMatch.fullPath, '.')
1422-
const toPath = dest.to
1423-
? this.resolvePathWithBase(fromPath, `${dest.to}`)
1424-
: this.resolvePathWithBase(fromPath, '.')
1422+
const destFromPath = dest.from && this.resolvePathWithBase(dest.from, '.')
1423+
1424+
const toPath = destFromPath
1425+
? this.resolvePathWithBase(destFromPath, `${dest.to ?? "."}`)
1426+
: this.resolvePathWithBase(fromPath, `${dest.to ?? "."}`)
14251427

14261428
const routeIsChanging =
14271429
!!dest.to &&
1428-
!comparePaths(dest.to.toString(), fromPath) &&
1429-
!comparePaths(toPath, fromPath)
1430+
(
1431+
comparePaths(destFromPath ?? fromPath, fromPath) ?
1432+
(
1433+
!comparePaths(toPath, fromPath)
1434+
) :
1435+
(
1436+
!comparePaths(toPath, destFromPath!) ||
1437+
!comparePaths(toPath, fromPath)
1438+
)
1439+
)
14301440

14311441
// If the route is changing we need to find the relative fromPath
14321442
if (dest.unsafeRelative === 'path') {

0 commit comments

Comments
 (0)