Support passing a custom History API object for createBrowserRouter, or expose a createRouter-like function for external use #14637
fionamatthews-wk
started this conversation in
Proposals
Replies: 1 comment
-
|
After further investigation, createRouter is exported as Looking at parseHydrationData, its simple enough that we could reproduce it ourselves, but it would be nice to have in the case where we cannot get a customizable history property. Beyond this, I've thrown together an idea that seems to be working effectively both for react-router and tanstack. Since both accept window objects, we can take advantage of that. We created a Proxy wrapping the window, which:
|
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
We are in the process of prototyping a buildout of a microfrontend architecture with the design goal that all microfrontends are as isolated as possible from each other and are free to bring whatever technologies fit their needs.
Part of this means microfrontends that perform routing should bring their own routers, TanStack or React-Router, or whatever else they may choose. These are passed a base url that is supplied by the host app, and they limit their routing to within that base path.
For the most part, this works great. The host (using react-router data mode) has routes registered for all the paths the microfrontends live on, and lazy-loads their asset bundles when the routes are hit. It uses wildcard paths, so if the microfrontend chooses, it can perform its own sub-routing. So long as the microfrontend appropriately configures its router with that base path, everything works great!.
...Except not so much, because it turns out the host's router is never updated when the sub-router changes the url, leading to issues when the host either wants to know the current full URL, or wants to get or set query parameters.
The issue we are running into is that the only method react-router uses to synchronize its state with the url is 'popstate' (and 'hashchanged'). And unfortunately, these do not get raised when the URL is programmatically changed through history.push and history.replace. As a consequence, child routers in microfrontends are not updating the host's router.
And this becomes a problem when query parameters are involved. If the host wants to set a query parameter, react-router will push the entire state with its internal state's last known url. This means that if the microfrontend's sub-router has navigated to a different sub-page, that navigation is wiped out and the user is ripped back to the very first page that loaded when they navigated to that microfrontend.
In order to address this, we want to be able to pass our own history API instance to react-router's createBrowserRouter function. By creating and using a single history API instance across all routers in all microfrontends, all routers should now get the listen() registered callback called from all other router's operations, keeping everything in sync.
For the 'old' pre-data-mode method of using react-router, this was easily solved using the react component. We already ran into this issue with older projects that pre-dated data mode where we needed to trigger routing changes from outside any react context (and thus outside the ability to call useNavigate), and we solved it by creating our own history API object, passing that, and then using that history api object whenever an external system needed to change the route. The system looked something like this:
This is, to a greater or lesser extent, an unrolled
<BrowserRouter>, except we pass our own history object (which is shared outside of this file) to react-router. And this worked! Any navigation happening external to react-router using the history object would propagate into react-router itself. Awesome!Unfortunately, it seems 'data mode' via createBrowserRouter no longer supports this method, as the history value passed to createRouter is not configurable. This wouldn't be so much of an issue if
createRouterwere itself exposed for us to use, but both it and the few helper functions it needs are not public API.I would like to request the ability to pass history objects to createBrowserRouter. This is a feature that pre-data-mode react-router supported, and is also supported by TanStack, another router we are investigating and wish to support for our downstream microfrontends.
Beta Was this translation helpful? Give feedback.
All reactions