Thank @FutianShen. But his/her answer is incorrect if you have nested routes. I tried to fix its issue:
const routes = (
<Route path='/' element={<Root />}>
<Route index element={<Index />} />
<Route path=':orgName' element={<Org />}>
<Route ...>
...
</Route>
</Route>
</Route>
)
const appRoutes = createRoutesFromElements(routes)
function usePathPattern() {
const location = useLocation()
return useMemo(
() =>
matchRoutes(appRoutes, location)
.map(({ route: { path } }) => path)
.filter(Boolean)
.join('/')
.replaceAll(/\/\*?\//g, '/'),
[location],
)
}
This also works for nested routes.
Sample output:
/:orgName/projects/:projectSlug/vms/:vmUid/*
But if you don't have access to your appRoutes
object, I don't have any idea except using UNSAFE_RouteContext
(thank @RichN).
I tried to improve/fix his/her answer and also make it compatible with react-router v6.4+:
import { UNSAFE_RouteContext } from 'react-router-dom'
function usePathPattern() {
let lastRouteContext = useContext(UNSAFE_RouteContext)
while (lastRouteContext.outlet) lastRouteContext = lastRouteContext.outlet.props.routeContext
return lastRouteContext.matches
.map(({ route: { path } }) => path)
.filter(Boolean)
.join('/')
.replaceAll(/\/\*?\//g, '/')
}
But ...
... if you (like me) need to know the pattern that matched until here (where usePathPattern()
is used), NOT the full-pattern, then you don't need to traverse into route-context to find the last one (findLastNode
in @RichN answer).
So this would be enough:
import { UNSAFE_RouteContext } from 'react-router-dom'
function useRoutePattern() {
const routeContext = useContext(UNSAFE_RouteContext)
return useMemo(
() =>
routeContext.matches
.map(({ route: { path } }) => path)
.filter(Boolean)
.join('/')
.replaceAll(/\/\*?\//g, '/'),
[routeContext.matches],
)
}
Sample output:
# In a near-to-root component:
/:localeCode/:orgName/
# In a very nested component (for the same URL):
/:localeCode/:orgName/iaas/:projectSlug/vms/:vmUid/