Prior to react-router 6.4, I was happily declaring my routes using the <Routes>
and <Route>
components.
Wherever I wanted some route-dependent rendering, I could use these 2 components.
I was able to nest routes, again by using the <Routes>
and <Route>
components in a nested component. I was also able to even use multiple Routes
containers next to each other in 1 component.
I like this a lot because it keeps things small, nested routes can be handled in the nested component and don't need to bloat the root component. But most of all I like it because seeing the routes in your code where they actually will be rendered makes the code very readable and easy to visualize for me.
Now with react-router 6.4 it seems like they are moving more to a configuration based style of routing, where you define all routes on a root level. In some interviews it's clear that the maintainers are proud that they can now define nested routes on a root level. https://reactrouter.com/en/main/start/overview#nested-routes
With that approach, you need to use the <Outlet/>
component in spots where you want to render the nested route. When reading the code you to cross reference those Outlet
locations with the configuration on the root level to figure out what is rendered where, which makes things much harder to visualize mentally.
So here's my question: what are the advantages of using such a configuration approach. Any guesses to why the maintainers are taking this road?
CodePudding user response:
Clarifications
First, since the introduction of
react-router@6
you could use a routes configuration via theuseRoutes
hook or just use the RRD components and define the JSX for them. This is the "configuration based" vs "component based" routing you describe. It's nothing new in RRDv6.4.Configuration Example:
import { useRoutes } from 'react-router-dom'; const routes = [ { element: <Layout />, children: [ { path: "/foobar", element: <FooBar /> }, { path: "/foo", element: <Foo /> }, { path: "/bar", element: <Bar /> }, ], }, { path: "/", element: <Home /> }, ]; const App = () => { const appRoutes = useRoutes(routes); return appRoutes; };
Component Example:
import { Routes, Route } from 'react-router-dom'; const App = () => ( <Routes> <Route element={<Layout />}> <Route path="/foobar" element={<Foobar />} /> <Route path="/foo" element={<Foo />} /> <Route path="/bar" element={<Bar />} /> <Route> <Route path="/" element={<Home />} /> </Routes> );
I believe somewhere in the older RRD docs outright stated that the
Routes
component was implemented using theuseRoutes
hook under-the-hood by converting the JSX you pass as children into a routes configuration object.Second, what you are describing as "nested routes" are actually called descendent routes. Nested routes on the other hand are
Route
components directly rendering otherRoute
components that render their content into theOutlet
of their parent route.Descendent routes are good for code/route splitting.
Question
So here's my question: what are the advantages of using such a configuration approach. Any guesses to why the maintainers are taking this road?
What RRDv6.4 introduced was a new Data API which uses new Data Routers. See Picking a Router. Along with the new Data Routers came an updated Route
component with a bunch of new props/functionality to be used within these new Data Routers. Now herein lies the rub. In order for the Route
components to take advantage of the new Data APIs (action
, errorElement
, loader
, and shouldRevalidate
props) they necessarily need to be defined/configured when creating the Data Router. It doesn't matter if you use a routes "configuration" object with createBrowserRouter
or use JSX with both createBrowserRouter
and the createRoutesFromElements
utility, all the routes that need the Data APIs need to be declared here.
The "advantage" is that you can use the new Data APIs with these routes. Of course the library maintainers are going to advertise and highlight their newest products and features.
AFAIK, the Data APIs don't currently work with descendent routes because these can be dynamic and there's not an easy way for a Route
component to know what descendent Route
components might be rendered under it in its sub-ReactTree at run-time. This is why nested routes are paramount; it's trivial to setup and maintain the overhead of the Data APIs when you know ahead of time exactly what routes can be rendered and what data they may manage.