I'm using react-router-dom version 6.14.1 in a react/redux project. I got it working where I can specify routes in index.tsx by programatically creating a router:
import {
createBrowserRouter,
RouterProvider
} from "react-router-dom";
const router = createBrowserRouter([ /*...routes specified in code here... */ ]);
...and then rendering a RouterProvider component to which I pass the above router.
root.render(
<React.StrictMode>
<ThemeContextProvider>
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
</ThemeContextProvider>
</React.StrictMode>
);
I am hard-coding the routes passed to createBrowserRouter right now but what I really want to do is load them from my database via an API call. Unfortunately, I am using Redux's RTK query and I can't use that in index.tsx since it doesn't all seem to get set up until the "Provider" component gets loaded. So, I seem to have a chicken and egg situation where I can't get the routes from data until I load some components but I can't load the components until I do my root.render which needs the RouteProvider component set up with routes.
Is there a proper architecture/approach for this type of situation?
I've spent hours trying all kinds of weird things (like trying to load a dummy component that loads the route data using RTK query and then calling back to the parent index.tsx code (via a function I pass to the dummy component) to update the router variable or to just somehow trying to get a reference to that "router" object from the dummy component and pushing new items into its "routes" array) but nothing works. Feels like I'm just doing this all wrong. I just want to be able to, at any time, update the routes in that RouterProvider.
My suggestion would be to create a component that conditionally renders the RouterProvider
once the routing data has been fetched. It's similar to your answer but doesn't throw one ReactTree away for another.
Something like the following:
const App = () => {
const dispatch = useDispatch();
const routes = useSelector(.....);
useEffect(() => {
dispatch(fetchRoutes());
}, [dispatch]);
const router = useMemo(() => {
if (routes) {
return createBrowserRouter(routes);
}
return null;
}, [routes]);
return router
? <RouterProvider router={router} />
: (
... loading UI/UX ...
);
};
root.render(
<React.StrictMode>
<ThemeContextProvider>
<Provider store={store}>
<App />
</Provider>
</ThemeContextProvider>
</React.StrictMode>
);