Describe the bug
Sometimes it may be desired/required to have routes defined outside of react-router or the javascript app or they are just not all known to the app. For example asking the backend if it has something to serve for this route.
One of my usecases is content served from an API with thousands of routes, some of which are only available to certain users and deciding which user can access what route is done on-the-fly by the backend.
This use-case can be served by adding a route like this (where fetchRoute performs a request to the backend):
{
path: "*",
loader: ({pathname}) => (
queryClient.getQueryData(["route", pathname]) ?? queryClient.fetchQuery(["route", pathname], () => fetchRoute(pathname))
),
element: <RouteComponent />,
}
Unfortunately, a "*" route currently has the same cache key regardless of the pathname, and therefore will fetch the correct content the first route and then return the same content for all following routes.
(Using a route parameter ":foobar" will work for top-level routes, but does not cover n
levels of nested routes.)
To Reproduce
I don't think an example is necessary right now, but I'd be happy to create one if needed.
Expected behavior
I think we should consider both the route path "*"
aswell as the matched pathname for the cache key and re-run the loader for every pathname.
Workaround
My currently working workaround:
const loader = React.useCallback(({pathname}) => (
queryClient.getQueryData(["route", pathname]) ?? queryClient.fetchQuery(["route", pathname], () => fetchRoute(pathname))
), [])
const routes = React.useMemo(() => {
const _routes = []
const routePaths = ["/"]
let lastPath = ""
Array(26).fill().map((element, index) => String.fromCharCode("a".charCodeAt(0) + index)).map(char => {
lastPath = `${lastPath}/:${char}`
routePaths.push(`${lastPath}/`)
})
routePaths.reverse().forEach((path) => {
_routes.push({
loader,
element: <RouteComponent />,
path,
})
})
return _routes
}, [])
This will produce the following 27 routes, all with the same loader and element:
/
/:a/
/:a/:b/
/:a/:b/:c/
- ...
/:a/:b/:c/:d/:e/:f/:g/:h/:i/:j/:k/:l/:m/:n/:o/:p/:q/:r/:s/:t/:u/:v/:w/:x/:y/:z/