Component Missing when Nested inside of Route in React Router v6 - javascript

When nesting the ChoosePlayer component inside a Route using React Router v6,
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players">
<Route element={<ChoosePlayer />} />
// <--- Some dynamically generated routes here for /players/{playerName}
// These inner routes shows a modal in addition to ChoosePlayer in the background
</Route>
</Routes>
</BrowserRouter>
the ChoosePlayer component does not render when we are on the url http://localhost:3000/players or http://localhost:3000/players/reacher.
As a sanity check, ChoosePlayer component is rendered at http://localhost:3000/chooseplayer when we have
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/chooseplayer" element={<ChoosePlayer />} />
</Routes>
</BrowserRouter>
and at http://localhost:3000/players when index is added to its Route component, but this prevents ChoosePlayer from showing up at http://localhost:3000/players/reacher
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players">
<Route index element={<ChoosePlayer />} />
// <--- Some dynamically generated routes here for /players/{playerName}
// These inner routes shows a modal in addition to ChoosePlayer in the background
</Route>
</Routes>
</BrowserRouter>
Why is it not rendering in the first example? Is there a way to do this in React Router v6? I think this approach works in React Router v5.
Thanks!

So I've gathered you want to render this ChoosePlayer component with the path is "/players" and also when on some "/players/*" path. In this case you are treating ChoosePlayer more as a layout component that renders a set of nested routes.
Issue
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players">
<Route element={<ChoosePlayer />} />
// <--- Some dynamically generated routes here for /players/{playerName}
// These inner routes shows a modal in addition to ChoosePlayer in the background
</Route>
</Routes>
</BrowserRouter>
The ChoosePlayer route is missing a path for matching and rendering.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/chooseplayer" element={<ChoosePlayer />} />
</Routes>
</BrowserRouter>
ChoosePlayer is matched and rendered, but isn't on a "/players/*" route and doesn't have any nested children routes.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players">
<Route index element={<ChoosePlayer />} />
// <--- Some dynamically generated routes here for /players/{playerName}
// These inner routes shows a modal in addition to ChoosePlayer in the background
</Route>
</Routes>
</BrowserRouter>
An an index route, ChoosePlayer is matched and rendered when the path is exactly "/players", but is excluded from being rendered when matching and rendering one of the other nested routes.
Solution
I suggest moving ChoosePlayer up into the root "/players" route and render an Outlet component for the nested routes to be rendered into.
Example:
import { Outlet } from 'react-router-dom';
const ChoosePlayer = () => {
// ...any component business logic...
return (
<div /* any container props/styling/etc... */>
{/* Common Choose Players UI */}
<Outlet /> // <-- nested routes render into here
</div>
);
};
...
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/players" element={<ChoosePlayer />} >
// <--- Some dynamically generated routes here for /players/{playerName}
// These inner routes shows a modal in addition to ChoosePlayer in the background
</Route>
</Routes>
</BrowserRouter>
You can read more about layout routes here.

Related

React router dom does not redirect on the default child route [duplicate]

I just can't find a way to set a default route with react-router v6
Is it because it's not good programming anymore?
Can somebody tell me why?
Thanks in advance
Rafael
If I understand your question about a "default" route correctly then I am interpreting this as one of the following:
Use an index route:
You can wrap a set of routes in a layout route and specify an index route:
<Routes>
<Route path="/*">
<Route index element={<ComponentA />} />
<Route path="pathB" element={<ComponentB />} />
<Route path="pathC" element={<ComponentC />} />
</Route>
</Routes>
The index route is the route that will be matched and rendered when the path exactly matches the root parent route's path.
Redirect to a "default" route if no other routes match:
You can also render a redirect to the route you consider to be the "default" route.
<Routes>
<Route path="/pathA" element={<ComponentA />} />
<Route path="/pathB" element={<ComponentB />} />
<Route path="/pathC" element={<ComponentC />} />
<Route path="*" element={<Navigate to="/pathA" replace />} />
</Routes>
TLDR;
use <Route index element={<Navigate to="/dashboard" />} />
index: default computed route.
<Navigate to="whatever you want"/>: is used to navigate to a another already declared path.
LR;
I found an easy way to redirect to a default component using index & Navigate combined.
In my situation I had used React Router V6.6.2 with:
createBrowserRouter(
createRoutesFromElements(...))
The routes look like this
/* All imports go here */
const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<AuthLayout />}>
<Route element={<RrotectedLayout />}>
<Route path="/" element={<MainLayout />}>
<Route index element={<Navigate to="/dashboard" />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="projects" element={<Projects />} />
<Route path="users" element={<Users />} />
<Route path="notifications" element={<Notification />} />
<Route path="settings" element={<Settings />} />
<Route
path="*"
element={<Navigate to="/dashboard" replace={true} />}
/>
</Route>
</Route>
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
</Route>,
),
{},
)
export default function App() {
return (
<>
<RouterProvider router={router} />
</>
)
}
Now when you access your application, React router will figure out which index your application needs to point to, and since your index contains a Navigation to a specific path, you'll be redirect to that path by default. you don't need to specify a specific component (element) in this situation because you don't wanna loose the link to it.
I actually found the answer here but I just wanna share my solution if it helps someone with theirs.
You can set path='*' to make a default route. The index route deals a parent route ("/") but doesn't deal with routes which should otherwise return a 404 status.
if (!token) {
// This router will handle my public routes. Anything else is going to redirect to AuthPage without losing the previous route typed.
return (
<BrowserRouter>
<Routes>
{/* Auth */}
<Route path="/">
<Route exact path="recover" element={<UnknownPage />} />
// Default route
<Route path="*" element={<AuthPage setToken={setToken} />} />
</Route>
</Routes>
</BrowserRouter>
);
}
// This router is inside my application. Only logged users will get here.
return (
<BrowserRouter>
<Routes>
{/* My base page is just some fixed structure like Header, Sidebar and Footer. For this problem you can ignore it. */}
{/* BasePage */}
<Route path="/*" element={<BasePage logout={logout} />}>
{/* This is my specific users route */ }
{/* Users */}
<Route path="users">
<Route path="" element={<UsersPage />} />
<Route path=":id" element={<UserInfoPage />} />
</Route>
{/* Anything else is going to show this page. Even random words like: http:localhost:3000/anything-asdvasd */}
{/* Default Route */}
<Route path="*" element={<UnknownPage />} />
</Route>
</Routes>
</BrowserRouter>
);
Using parent routes like I used in my users routes makes it easier to scope your default routes.
If you are using createBrowserRouter you can set the default route in following way.
As per docs component loads children of parent. So
const router = createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{
path: "/",
element: <Home />,
},
{
path: "/home",
element: <Home />,
},
],
},
],);
If you are using createBrowserRouter you can set the default route in following way.
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{ index: true, element: <Navigate to="/calculation" replace /> },
{ path: "calculation", element: <Calculation /> },
{ path: "calendar", element: <Calendar /> },
{ path: "profile", element: <Profile /> },
],
},
]);

React Router does not resolving on parameters route

I'm using react-router-dom V6
<Router>
<Routes>
<Route path="/">
<Route path="example" element={<h1>Example</h1>}>
<Route path=":id" element={<p>cool</p>} />
</Route>
</Route>
</Routes>
</Router>
"http://localhost:3000/example " renders Example so far good, but
"https://localhost:3000/example/34" renders nothing.
What is the problem?
Please provide a why and how to solve this issue.
When nesting routes, the parent routes need to render an Outlet component for the nested routes to render their element content into. Route components render an Outlet by default when no element prop is provided, so this is why the "/example" route works.
Example:
import {
BrowserRouter as Router,
Routes,
Route,
Outlet,
} from 'react-router-dom';
...
<Router>
<Routes>
<Route path="/">
<Route
path="example"
element={(
<>
<h1>Example</h1>
<Outlet />
</>
)}
>
<Route path=":id" element={<p>cool</p>} />
</Route>
</Route>
</Routes>
</Router>
If you don't want to render the "Example" component at the same time as its children, then render it alone on its own index route.
Example:
<Router>
<Routes>
<Route path="/">
<Route path="example">
<Route index element={<h1>Example</h1>} />
<Route path=":id" element={<p>cool</p>} />
</Route>
</Route>
</Routes>
</Router>
For more details, see:
Routes and Route
Layout Routes
Outlet

Changing from v5 to v6, how to make it work with relative paths?

My application has scenarios where we need several routes to "pass" through a component to only then render the specifics, not only that but also situations where something is shown for the "parent" route and then split for the children...
It is imperative to note that we don't have a single "route config" file, and instead our routes are where we need them.
This was possible with v5, but I am very confused about how to get this accomplished with the new version.
So, currently we have stuff such as:
App.js
function App = () => {
return (
<Switch>
<Route exact path={['/', '/2', '/more-info']} component={Login} />
<Route path="/(main|settings|notifications)" component={AuthenticatedUser} />
<Redirect from="*" to="/404" />
</Switch>
);
}
AuthenticatedUser.js
function AuthenticatedUser= () => {
{... lots of common code}
return (
<div>
{...common html}
<Switch>
<Route exact path="/main" component={Main} />
<Route path="/settings" component={Settings} />
<Route path="/notifications" component={Notifications} />
</Switch>
</div>
);
}
Settings.js
function Settings= () => {
{... lots of common code}
return (
<div>
{...common html}
<Switch>
<Route exact path="/settings/basic" component={Basic} />
<Route exact path="/settings/notifications" component={Notifications} />
</Switch>
</div>
);
}
Now, with the relative from the parent, I am not able to get the same structure, I am also confused about how to get the routes split into separate files not even talking about the regex situation that I am guessing the solution is to duplicate the lines as many times as I have items in that regex...
You have basically 2 options when it comes to declaring the routes and sharing common UI:
Use layout routes and nested Route components.
Render routed components that render descendent routes in another Routes component wrapping descendent Route components.
Using layout and nested routes
Convert AuthenticatedUser into a layout route. Layout routes render an Outlet for nested routes to render their matched element into.
import { Outlet } from 'react-router-dom';
function AuthenticatedUser = () => {
{... lots of common code}
return (
<div>
{...common html}
<Outlet />
</div>
);
};
Convert Settings also into a layout route component.
import { Outlet } from 'react-router-dom';
function Settings = () => {
{... lots of common code}
return (
<div>
{...common html}
<Outlet />
</div>
);
};
App
import { Routes, Route, Navigate } from 'react-router-dom';
function App = () => {
return (
<Routes>
<Route path="/" element={<Login />} />
<Route path="/2" element={<Login />} />
<Route path="/more-info" element={<Login />} />
<Route element={<AuthenticatedUser />}>
<Route path="/main" element={<Main />} />
<Route path="/settings" element={<Settings />}>
<Route
path="basic" // "/settings/basic"
element={<Basic />}
/>
<Route
path="notifications" // "/settings/notifications"
element={<Notifications />}
/>
</Route>
<Route path="/notifications" element={<Notifications />} />
</Route>
<Route path="*" element={<Navigate to="/404" replace />} />
</Routes>
);
};
Using descendent routes
Here the parent routes need to render their route path with a trailing "*" wildcard matcher so descendent routes can also be matched. Descendent Routes components build their route paths relative to their parent Route path. I'd still suggest using AuthenticatedUser as a layout route for ease, otherwise you'll have a lot of code duplication since you'd need to wrap each route individually.
App
import { Routes, Route, Navigate } from 'react-router-dom';
function App = () => {
return (
<Routes>
<Route path="/" element={<Login />} />
<Route path="/2" element={<Login />} />
<Route path="/more-info" element={<Login />} />
<Route element={<AuthenticatedUser />}>
<Route path="/main" element={<Main />} />
<Route path="/settings/*" element={<Settings />} />
<Route path="/notifications" element={<Notifications />} />
</Route>
<Route path="*" element={<Navigate to="/404" replace />} />
</Routes>
);
};
Settings
import { Routes } from 'react-router-dom';
function Settings = () => {
{... lots of common code}
return (
<div>
{...common html}
<Routes>
<Route
path="/basic" // "/settings/basic"
element={<Basic />}
/>
<Route
path="/notifications" // "/settings/notifications"
element={<Notifications />}
/>
</Routes>
</div>
);
};

React router only show Navigation component if the current path is on any of the valid routes

For a invalid route, I want to show the NotFound component, but also not show the Navigation component:
const Layout: React.FunctionComponent = () => {
return (
<Router>
<Navigation />
<Switch>
<Route path="/explore" exact>
<ExploreIndex />
</Route>
<Route path="/explore/:id" exact>
<ExploreShow />
</Route>
<Route path="/" exact>
<Home />
</Route>
<Route component={NotFound} />
</Switch>
</Router>
);
};
If I go to /aaaaaaa, my NotFound component loads but so does my Navigation. How can I not have Navigation render for such routes?
What about just rendering it as another route?
<Route path={['/explore', '/explore/:id', '/']} exact component={Navigation} />
It will not be rendered if the route does not match any of the routes listed in the path array.
You can add NavigationBar in the specific components rather than app.js. So for example if there is a about page, place NavigationBar on top of the component

relative routing in react-router

Did anyone know how to create for any path that ends with '/popup-image-:id' ? i have website where each image could be opened in popup on any page. So, if i couldn't implement '/popup-image-:id' i have to double each route in my website to place inside. I would like avoid redundance and fragility.
my current routing:
<Router history={browserHistory}>
<Route path="/" component={Base}>
<Route path="profile" component={Profile}>
<Route path="popup-image-:id" component={Full_Piture} />
</Route>
<Route path="feed" component={Feed_List}>
<Route path="popup-image-:id" component={Full_Piture} />
</Route>
<Route path="user-:id" component={User_Page}>
<Route path="popup-image-:id" component={Full_Piture} />
</Route>
</Route>
</Router>
As u can see i had to produce a lot of duplicated code. If i could write something like <Route path="*/popup-image-:id" component={Full_Piture} /> it will be much cleaner and solid code
Is the parent <Route> also supposed to match? If it isn't, you should be able to use the ** syntax.
<Route path="/**/popup-image-:id" component={Full_Picture} />
If the parent <Route> should also match, you could create a <RouteWithPopup> component that returns a <Route> which has the <Route path='popup-image-:id'> as its child.
const RouteWithPopup = (props) => {
return (
<Route {...props}>
<Route path="popup-image-:id" component={Full_Picture} />
</Route>
)
}
Then you could turn your route config into:
<Router history={browserHistory}>
<Route path="/" component={Base}>
<RouteWithPopup path="profile" component={Profile} />
<RouteWithPopup path="feed" component={Feed_List} />
<RouteWithPopup path="user-:id" component={User_Page} />
</Route>
</Router>

Categories

Resources