relative routing in react-router - javascript

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>

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 /> },
],
},
]);

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>
);
};

Component Missing when Nested inside of Route in React Router v6

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.

Routes - react-router v5 - hitting NotFound always

I am trying to add a Landing page for my application. After the authorization. The routes after landing page is always hitting the NotFound route. I am unable to figure out the reason.
The initial routes are :
const Initial = ({ settings }) => {
const store = setupStore(reducers, { settings });
return (
<Provider store={store}>
<AuthProvider appSettings={settings}>
<Router basename={settings.AppContextPath}>
<MainLayout>
<Switch>
<Route
path={LOGIN_URL}
component={Login}
/>
<Route
path={LOGOUT_URL}
component={Logout}
/>
<Route
exact={true}
path={SILENT_RENEW_URL}
component={SilentRenew}
/>
<AuthenticatedRoute>
<App>
<Switch>
<Route
exact path="/"
component={LandingPage}
/>
<Route component={NotFound} />
</Switch>
</App>
</AuthenticatedRoute>
</Switch>
</MainLayout>
</Router>
</AuthProvider>
</Provider>
);
};
In the LandingPage component I have added rest of the routes which should be based on clicking the Link from Landing Page:
const FeatureRoutes = () => {
return (
<Switch>
<Route
path={`/:Id/feature1`}
component={Feature1}
/>
<Route
path={`/:Id/feature2`}
component={Feature2}
/>
<Route exact path={`/:Id`} component={Features} />
</Switch>
);
};
export default FeatureRoutes;
Clicking the link navigates correctly to url with "/:id", but it shows NotFound page.
Please let me know which part I am doing wrong.
i could solve it to go till 'Features' route by changing the route so :
<AuthProvider appSettings={settings}>
<Router basename={settings.AppContextPath}>
<MainLayout>
<Switch>
Route
path={LOGIN_URL}
component={Login}
/>
<Route
path={LOGOUT_URL}
component={Logout}
/>
<Route
exact={true}
path={SILENT_RENEW_URL}
component={SilentRenew}
/>
<AuthenticatedRoute>
<App>
<Switch>
<Route
exact path="/"
component={LandingPage}
/>
<Route
path={'/:Id'}
component={FeatureRoutes}
/>
</Switch>
</App>
</AuthenticatedRoute>
</Switch>
</MainLayout>
</Router>
</AuthProvider>
</Provider>
);
};
I moved the NotFound route into FeatureRoutes.

Nested React Router : hide parent component on showing nested child component

Being a beginner in reactJS, I want to know how to hide parent component when i route to child component URL
Assume a scenario:
User is at "/house" as this:
<Route path="/house" component={House} />
when user clicks a house grid he navigates to "/house/flat/:flatID". Inside House component
<Route
path={`${this.props.match.url}/flat/:flatId`}
render={() => <div>Each Flat details</div>}
/>
then both child and parent components are visible like this:
So what i want is to show only flat component when user navigates to "/house/flat:FlatId". Please suggest me anything that helps ! Any links to article so that i can learn and achieve such functionality.
Code:
App.js
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/account" component={Account} />
<Route path="/gharjagga" component={GharJagga} />
</Switch>
House.js
onGharGridClick= id => {
<Redirect to={`${this.props.match.url}/flat/${id}`} />;
};
return (
<Route
path={`${this.props.match.url}/flat/:fkatId`}
render={() => <div>Ghar Each</div>}
/>
);
You can achieve it different ways
define routes in the parent component, I think this is the best option.
<Switch>
<Route path="/account" component={Account} />
<Route path="/house/flat/:flatId" component={FlatComponent}/>
<Route path="/house" component={House} />
<Route path="/" component={Home} />
</Switch>
Note: instead of using exact, order your routes based on priority, that will make the route to redirect to next matching route if any typo in the route entered
You can make House as separate route component, and nest the routes inside that component
// Routes
<Switch>
<Route path="/account" component={Account} />
<Route path="/house" component={House} />
<Route path="/" component={Home} />
</Switch>
// House component
class House extends React. Components {
render() {
return (
<Switch>
<Route path="/house/flat/:flatId" render={() => <div>Each Flat details</div>} />}/>
<Route path="/house" component={HouseGridComponent} />
</Switch>
)
}
}
you can check whether the route has flatId and hide the elements, in your House component you can check this.props.match.params.flatId, if the flatId is set you can hide that House Component.
// House Component
class House extends React. Components {
render() {
return (
<div>
{
!this.props.match.params.flatId?
<h1>House Component</h1>:
null
}
<Route path={`${this.props.match.url}/flat/:flatId`} render={() => <div>Each Flat details</div>} />
</div>
)
}
}
The solution is to raise "/house/flat/:flatId" to the same level as "/house".
<Switch>
<Route exact path="/" component={Home} />
<Route path="/account" component={Account} />
<Route path="/house/flat/:flatId" render={() => <div>Each Flat details</div>}/>
<Route path="/house" component={House} />
</Switch>

Categories

Resources