Nested route is not working as expected - React router - javascript

My code is
<Switch>
<Route path='/' exact render={() => <Redirect to='/route' />} />
<Route path='/route/' exact render={() => <Component />} />
<Route path='/route/subroute/:id+' exact render={() => <Component1/>} />
<Route path='/route/subroute/subsubroute1/:id+' exact render={() => <Component2 />} />
<Route path='/route/subroute/subsubroute2/:id+' exact render={() => <Component3 />} />
<Route path='/route/:subroute+' exact render={() => <Component4 />} />
<Route render={() => <Redirect to='/notfound' />} />
</Switch>
Now when I am hitting history.push(/route/subroute/id) it is working
But when I try to open history.push(/route/subroute/subroute1/id) it is not opening page for me. I tried to go through the documents but all things seem to be in place. What am i missing.

+ operator at /route/subroute/:id+ makes you match /route/subroute/subsubroute1/123 for Component1 route, with a param subsubroute1/123.
remove the + and it should work as expected.
alternatively, if you really need to use +, you can move down Component1, and Component2 will be matched properly since react-router-dom matches the first route that matches the given path:
<Switch>
<Route path='/' exact render={() => <Redirect to='/route' />} />
<Route path='/route/' exact render={() => <Component />} />
<Route path='/route/subroute/subsubroute1/:id+' exact render={() => <Component2 />} />
<Route path='/route/subroute/subsubroute2/:id+' exact render={() => <Component3 />} />
<Route path='/route/subroute/:id+' exact render={() => <Component1/>} />
<Route path='/route/:subroute+' exact render={() => <Component4 />} />
<Route render={() => <Redirect to='/notfound' />} />
</Switch>

Related

How to load multiple routes in index file using react

I have a following index file -
<BrowserRouter>
<Routes>
<Route path="/" element={Home}
<Route element={Navigation}
</Routes>
</BrowserRouter>
I have multiple routes which I have to load So For that I have written a component:
const Navigation = () => {
<>
<Route path="/not-available-primary" element={NotAvaliable}/>
<Route path="/not-available-Secondary" element={Avaliable}/>
<Route path="/available-primary" element={Primary}/>
<Route path="/available-Secondary" element={Secondary}/>
<Route path="/direct-debit-primary" element={DirectDBB}/>
<Route path="/direct-debit-secondary" element={DirectDBBSecondary}/>
</>
}
But here, it does not render the component and gives no routes matched for location.
How do I fix this ?
You have to give a Component to the element prop, like this:
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/*" element={<Navigation />} />
</Routes>
</BrowserRouter>
and
const Navigation = () => {
return (<>
<Route path="/not-available-primary" element={<NotAvaliable />}/>
<Route path="/not-available-Secondary" element={<Avaliable />}/>
<Route path="/available-primary" element={<Primary />}/>
<Route path="/available-Secondary" element={<Secondary />}/>
<Route path="/direct-debit-primary" element={<DirectDBB />}/>
<Route path="/direct-debit-secondary" element={<DirectDBBSecondary />}/>
</>)
}
or
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/*">
<Route path="not-available-primary" element={<NotAvaliable />}/>
<Route path="not-available-Secondary" element={<Avaliable />}/>
<Route path="available-primary" element={<Primary />}/>
<Route path="available-Secondary" element={<Secondary />}/>
<Route path="direct-debit-primary" element={<DirectDBB />}/>
<Route path="direct-debit-secondary" element={<DirectDBBSecondary
/>}/>
</Route>
</Routes>
</BrowserRouter>
Note that in the seconds solution (nested routes) I erased the "/".
Documentation here
in your index file
<Route path="/" compoment={Home}
if "Navigation" is your class
const Navigation = () => {
return (
<>
<Routes>
<Route path="/not-available-primary" component={NotAvaliable}/>
<Route path="/not-available-Secondary" component={Avaliable}/>
<Route path="/available-primary" component={Primary}/>
<Route path="/available-Secondary" component={Secondary}/>
<Route path="/direct-debit-primary" component={DirectDBB}/>
<Route path="/direct-debit-secondary" component={DirectDBBSecondary}/>
</Routes>
</>
)
}
Issue
It's unclear which version of react-router-dom you are trying to use. version 4/5 or 6, but you've syntax issues for either. The route rendering the Navigation component needs a path with a trailing "*" character to be matched at the root/base level so the descendent routes can then also be matched and rendered.
Using react-router-dom#5
Use the Switch component instead of the Routes component. The Route components use the component (or render or children function props). The home path "/" must exactly match in order for any other route to be matched when the path isn't exactly "/", e.g. "/not-available-primary".
<BrowserRouter>
<Switch>
<Route path="/" exact component={Home} />
<Route path="*" component={Navigation} />
</Switch>
</BrowserRouter>
...
const Navigation = () => {
<Switch>
<Route path="/not-available-primary" component={NotAvaliable} />
<Route path="/not-available-Secondary" component={Avaliable} />
<Route path="/available-primary" component={Primary} />
<Route path="/available-Secondary" component={Secondary} />
<Route path="/direct-debit-primary" component={DirectDBB} />
<Route path="/direct-debit-secondary" component={DirectDBBSecondary} />
</Switch>
};
Using react-router-dom#6
All Route components can only be rendered by a Routes component or another Route component in the case of route nesting. The Route component API changed significantly and there is now only a single element prop taking a ReactNode, a.k.a. JSX, as a value. In RRDv6 all routes are now always exactly matched and use a route ranking system; there is no longer an exact prop for Route components.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<Navigation />} />
</Routes>
</BrowserRouter>
...
const Navigation = () => {
<Routes>
<Route path="/not-available-primary" element={<NotAvaliable />} />
<Route path="/not-available-Secondary" element={<Avaliable />} />
<Route path="/available-primary" element={<Primary />} />
<Route path="/available-Secondary" element={<Secondary />} />
<Route path="/direct-debit-primary" element={<DirectDBB />} />
<Route path="/direct-debit-secondary" element={<DirectDBBSecondary />} />
</Routes>
};
At the first use <Switch> in Navigation component :
const Navigation = () => {
<Switch>
<Route path="/not-available-primary" component={NotAvaliable}/>
<Route path="/not-available-Secondary" component={Avaliable}/>
<Route path="/available-primary" component={Primary}/>
<Route path="/available-Secondary" component={Secondary}/>
<Route path="/direct-debit-primary" component={DirectDBB}/>
<Route path="/direct-debit-secondary" component={DirectDBBSecondary}/>
</Switch>
}
so, add the component to Route:
<BrowserRouter>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/login" exact component={LoginForm} />
<Route component={Navigation} />
</Switch>
</BrowserRouter>

How to send props using render in reachrouterdomv6?

I am learning from udemy but that course is from v5.I dont know what is equivalent to render and how to send props with it ? Also, in bottom of the code if element is undefined it should go to "NotFound" but its not working too.
<Routes>
<Route
exact
path="/home"
render={(props) => (
<ProductList
{...props}
products={this.state.products}
currentCategory={this.state.currentCategory}
info={productInfo}
addToCart={this.addToCart}
/>
)}
></Route>
<Route exact path="/cart" element={<CartList />} />
<Route element={<NotFound />} />
</Routes>
<Routes>
<Route exact path="/" element={<ProductList
{...this.props}
products={this.state.products}
currentCategory={this.state.currentCategory}
info={productInfo}
addToCart={this.addToCart}
/>}></Route>
<Route exact path="/cart" element={<CartList />} />
<Route path="*"element={<NotFound />} />
</Routes>
I solved.
In the v6 you can pass props directly to the element. No need to use render anymore
<Route exact path="/cart" element={<CartList {...props} />} />

How to go specific route without rendering some components in React?

I am using react-router-dom v6. I want my Login page to be rendered without the Sidebar and Topbar components. How to do it?
function App() {
return (
<Router>
<Container>
<Sidebar />
<Content>
<Topbar />
<MainContent>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
<Route path="/login" element={<Login />} />
</Routes>
</MainContent>
</Content>
</Container>
</Router>
);
}
I don't want my login page to render inside the MainContent but taking the whole page without Sidebar and Topbar.
I tried moving the Routes upper and have the login route above the sidebar but there is an error Error: [Sidebar] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
Since SideBar and TopBar appear to be part of a "layout", and you want a different "layout" specifically for "/login" then I suggest abstracting the container components into layout components. Each layout container should render an Outlet for their respective nested routes.
const AppLayout = () => (
<Container>
<Sidebar />
<Content>
<Topbar />
<MainContent>
<Outlet />
</MainContent>
</Content>
</Container>
);
const LoginLayout = () => (
<Container>
<Content>
<MainContent>
<Outlet />
</MainContent>
</Content>
</Container>
);
...
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<AppLayout />}>
<Route index element={<Home />} />
<Route path="users" element={<UserList />} />
<Route path="users/:id" element={<User />} />
<Route path="newUser" element={<NewUser />} />
<Route path="products" element={<ProductList />} />
<Route path="products/:id" element={<Product />} />
<Route path="newProduct" element={<NewProduct />} />
</Route>
<Route path="/login" element={<LoginLayout />}>
<Route index element={<Login />} />
</Route>
</Routes>
</Router>
);
}
function App() {
return (
<Router>
<Container>
{
Login || <Sidebar />
}
<Content>
{
Login || <Topbar />
}
<MainContent>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
<Route path="/login" element={<Login />} />
</Routes>
</MainContent>
</Content>
</Container>
</Router>
);
}
try conditional rendering. It works for me!

how to get params using react router dom v5.0.1

how to get params using react router dom v5.0.1 before I could get parameters, but now I can't get them, what's the problem?
<BrowserRouter>
<Route path='/' exact component={Login} />
<Route path='/register' component={Register} />
<Route path='/home' component={Home} />
<Route path='/all/merchant' component={AllMerchant} />
<Route path='/detail/merchant/:id_merchant' exact component={DetailMerchant} />
<Route path='/detail/merchant/promo/:id_merchant' component={MerchantByPromo} />
<Route path='/promo/voucher/by/merchant/' component={MerchantByPromoDetail} />
<Route path='/all/category' component={Category} />
<Route path='/category/id/:category_id' component={CategoryById} render = {props => <CategoryById {...props} key={this.props.match.params} /> }/>
<Route path='/promo/grid/view' component={PromoGridView} />
{/* params not found */}
<Route path='/promo/grid/view/by/category/:category_id' component={AllPromoGridView} />
<Route path='/detail/promo/:voucher_id/category_id/:category_id' component={PromoGridViewDetail} />
{/* params not found */}
<Route path='/account' component={Account} />
</BrowserRouter>
According to the react-router doc, it should be accessible in your Component via the props match:
Example:
// the route
<Route path="/:id" component={Child} />
/*
...
*/
//the component
function Child({ match }) {
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
);
}

Best way to proceed to have both public and private routes on a single web app using Keycloak and React router?

I had a web app where you had to be authentified to see the content. First thing you saw entering the website was the Keycloak login page rendered in Login.js, once you were logged in, you had access to Routes.js. Now I have to add public routes to this same application and I'm not sure how to proceed. Here's what I have so far.
Index.js
<Suspense fallback='loading'>
<BrowserRouter>
<PublicRoutes/>
</BrowserRouter>
</Suspense>
PublicRoutes.js
return (
<Switch>
<Route exact path="/" render={() => <Redirect to="/signup"/>}
/>
<Route exact path="/signup" render={() => <SignupPage header={HeaderNav}/>}
/>
<Route exact path="/login" render={() => <Login/>}
/>
</Switch>
);
Login.js
if(this.state.keycloak) {
if(this.state.authenticated) return (
<Routes keycloak={this.state.keycloak}/>
); else return (<div>Unable to authenticate!</div>)
}
return (
<div>Loading, please wait...</div>
);
Routes.js
<Switch>
<Route exact path="/" render={() => <Redirect to="/home"/>}
/>
<Route exact path="/home" render={() => <HomePage header={HeaderNav} sidebar={Sidebar}/>}
/>
<Route exact path="/catalog" render={() => <CatalogPage header={HeaderNav} sidebar={Sidebar}/>}
/>
<Route exact path="/query" render={() => <QueryPage header={HeaderNav} sidebar={Sidebar}/>}
/>
<Route exact path="/workshop" render={() => <WorkshopPage header={HeaderNav} sidebar={Sidebar}/>}
/>
<Route exact path="/dashboard/stats" render={() => <StatsPage header={HeaderNav} sidebar={Sidebar} keycloak={this.props.keycloak}/>}
/>
<Route exact path="/dashboard/keys" render={() => <KeysPage header={HeaderNav} sidebar={Sidebar} keycloak={this.props.keycloak}/>}
/>
<Route exact path="/dashboard/consumption" render={() => <ConsumptionPage header={HeaderNav} sidebar={Sidebar} keycloak={this.props.keycloak}/>}
/>
<Route exact path="/faq" render={() => <HelpPage header={HeaderNav} sidebar={Sidebar}/>}
/>
<Route exact path="/contact_us" render={() => <ContactPage header={HeaderNav} sidebar={Sidebar}/>}
/>
<Redirect from="/login" to="/home"/>
</Switch>
What I'd like is that the Routes.js file takes over the PublicRoutes.js file when the user is authentified. Right now, when the user logs in, he can still only see the public routes content.
Try lifting state up. Maybe Index.js can switch between 2 sets of routers?
You can pass state (Keycloak object) or callbacks (handleLogin) for Login.js if you need to
Example:
render() {
const { keycloak, authenticated } = this.state;
return
<div>
{authenticated ? (
<Secured keycloak={keycloak} />
) : (
<Public handleLogin={this.handleLogin} />
)}
</div>
}

Categories