I have a App.js file that includes all the routes that I have. I wanted to make use of React-Router data loader.
import React from 'react'
import { Routes, Route, Navigate, RouterProvider, createBrowserRouter, createRoutesFromElements} from 'react-router-dom'
import { ThemeProvider, CssBaseline } from '@mui/material'
import Login from './pages/global/Login'
import NoSidebarLayout from './pages/global/NoSidebarLayout'
import SidebarLayout from './pages/global/SidebarLayout'
import NotFound from './pages/NotFound'
import ComingSoon from './pages/ComingSoon'
import Home from './pages/Home'
import About from './pages/About'
import Contact from './pages/Contact'
import ItemRoutes from './pages/ItemRoutes'
import CountryRoutes from './pages/country/CountryRoutes'
import PortRoutes from './pages/port/PortRoutes';
import ServicesManagerRoutes from './pages/serviceManager/ServicesManagerRoutes';
import ChargeManagerRoutes from './pages/chargeManager/ChargeManagerRoutes';
import PortChargesRoutes from './pages/portCharges/PortChargesRoutes';
import PortsRoutes from './pages/ports/PortsRoutes';
import PrivateRoutes from './components/PrivateRoutes';
import { ColorModeContext, useAppTheme } from './theme'
import { Provider } from "react-redux";
import store from "./redux/store";
import CountryList, { loader as CountryListLoader } from './pages/country/CountryList';
import Country from './pages/country/Country'
const router = createBrowserRouter(createRoutesFromElements(
<Route>
<Route element={<PrivateRoutes />}>
<Route element={<SidebarLayout />}>
<Route path="/ui/" element={<Home />} />
<Route path="/ui/items/*" element={<ItemRoutes />} />
<Route path="/ui/countries/*" element={<CountryRoutes />} />
<Route path="/ui/ports/*" element={<PortRoutes />} />
<Route path="/ui/contact" element={<Contact />} />
<Route path="/ui/about" element={<About />} />
<Route path="/ui/service-manager/*" element={<ServicesManagerRoutes />} />
<Route path="/ui/charge-manager/*" element={<ChargeManagerRoutes />} />
<Route path="/ui/port-charges/*" element={<PortChargesRoutes />} />
<Route path="/ui/port-manager/*" element={<PortsRoutes />} />
<Route path="/ui/coming-soon/*" element={<ComingSoon />} />
<Route path="*" element={<NotFound />} />
</Route>
</Route>
<Route element={<NoSidebarLayout />}>
<Route path="/ui/login" element={<Login />} />
<Route path="*" element={<NotFound />} />
</Route>
</Route>
))
const App = () => {
const [theme, colorMode] = useAppTheme();
return (
<Provider store={store}>
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<CssBaseline />
<div className="app">
<RouterProvider router={router}/>
</div>
</ThemeProvider>
</ColorModeContext.Provider>
</Provider>
)
}
export default App
on the CountryRoutes I have this, which includes the loader
import React from 'react'
// import { Routes, Route } from 'react-router-dom';
import { Routes, Route, Navigate, RouterProvider, createBrowserRouter, createRoutesFromElements } from 'react-router-dom'
import CountryList, { loader as CountryListLoader } from './CountryList';
import Country from './Country';
const CountryRoutes = () => (
<Routes>
<Route >
<Route index element={<CountryList />} loader={CountryListLoader} />
<Route path=":id" element={<Country />} />
</Route>
</Routes>
);
export default CountryRoutes;
With this setup the loader is not working but when I directly add the children routes to the parent like this,
const router = createBrowserRouter(createRoutesFromElements(
<Route>
<Route element={<PrivateRoutes />}>
<Route element={<SidebarLayout />}>
<Route path="/ui/" element={<Home />} />
<Route path="/ui/items/*" element={<ItemRoutes />} />
<Route path="/ui/countries/*">
{/* Directly adding the children routes */}
<Route index element={<CountryList />} loader={CountryListLoader} />
<Route path=":id" element={<Country />} />
</Route>
<Route path="/ui/ports/*" element={<PortRoutes />} />
<Route path="/ui/contact" element={<Contact />} />
<Route path="/ui/about" element={<About />} />
<Route path="/ui/service-manager/*" element={<ServicesManagerRoutes />} />
<Route path="/ui/charge-manager/*" element={<ChargeManagerRoutes />} />
<Route path="/ui/port-charges/*" element={<PortChargesRoutes />} />
<Route path="/ui/port-manager/*" element={<PortsRoutes />} />
<Route path="/ui/coming-soon/*" element={<ComingSoon />} />
<Route path="*" element={<NotFound />} />
</Route>
</Route>
<Route element={<NoSidebarLayout />}>
<Route path="/ui/login" element={<Login />} />
<Route path="*" element={<NotFound />} />
</Route>
</Route>
))
Notice that I directly added the children route to the "/ui/countries/*"
. I am a bit confused to which router should I add the loader.
2
Answers
This bug is most likely being caused by the use of the
<Routes/>
component.Per the docs,
<Routes/>
is seldom used in conjunction with a data router such ascreateBrowserRouter()
. Why? Because of this well-hidden tidbit of knowledge, which can be paraphrased thus—Okay, so how can you ensure all routes can access the data APIs? You have two options.
<Route>
defined within a<Routes/>
component to the top level, Continue shifting them upward one by one until all routes are defined as direct children of the RouterProvider. In your case this iscreateBrowserRouter()
.Although unaware, you did this by directly adding the children’s routes to the parent; this also explains why the loader works.
<Route/>
component to create a pathless layout route around the target routes instead of using<Routes/>
. However, for this to work, an must be added for the child routes to render, especially the index. Index routes, as defined in the docs, render "in their parent route’s outlet at the parent route’s path."In your case, you could edit the
<CountryRoutes />
component to look something like this:Then, in the
<App />
component, you could nest the index and dynamic country routes.Of course, although these edits should provide a good starting point, you will likely need to tweak them a bit for best results.
This is the intended and expected behavior.
Route loaders and actions only work in routes declared in the Data router, they are not accessible in descendent routes, e.g. routes rendered in a nested
Routes
component of a routed component.See Routes component:
The second snippet works because you have included the countries routes directly in the
createBrowserRouter
router declaration.