Nested navigation with params is not working in react-router-dom - reactjs

i am trying to use nested routing inside my application. in this case shop page is located on /shop url and i want to render CollectionsOverview on /shop/ and CollectionPage on /shop/:collection and the CollectionsOverview is working fine but
CollectionPage is not working. i even tried to use hardcoded path like /shop/abc but its still not working.. please help
version of my react-router-dom is ^5.0.0
import React from 'react';
import {Route, Switch,Router} from 'react-router-dom';
function CollectionPage() {
return (
<div className="App">
CollectionPage
</div>
);
}
function CollectionsOverview() {
return (
<div className="App">
CollectionsOverview
</div>
);
}
export default function ShopPage({match}){
return(
<div className='shop-page'>
<Router>
<Switch>
<Route exact path={`${match.path}`} component={CollectionsOverview}/>
<Route exact path={`${match.path}/:collectionId `} component={CollectionPage}/>
</Switch>
</Router>
</div>
)
}

Try this, since you already have a Router wrapping your App component, you don't need another one having it's own context in the ShopPage, since it's -supposedly- is originally routed from App, in the context of the Router that lives in the App
export default function ShopPage({match}){
return (
<div className='shop-page'>
<Switch>
<Route exact path={`${match.path}`} component={CollectionsOverview}/>
<Route exact path={`${match.path}/:collectionId `} component={CollectionPage}/>
</Switch>
</div>
);
}
You'll need the Switch only to determine navigating decision for it's Route components

Related

React execute common code on each route change

I have a component included in my App.js and that component contains some common logic which needs to be executed.
So my App component JSX looks like
<CommonComponent />
{canRenderBool && (
<div class="container">
<Route exact path="/comp1">
<Comp1 />
</Route>
<Route exact path="/comp2">
<Comp2 />
</Route>
</div>
)
}
Now, I want that on each route transition (e.g. user clicks on a new route url), I want that the code in CommonComponent (non-routable component) gets triggered.
This common component has logic which returns a boolean variable and I kind of render/not render Comp1/Comp2 i.e. all the routes based on that boolean
What is the best way I can handle it. I want to avoid having that code defined/invoked in each component manually?
Also most of my components are functional/hooks based.
In case you are using functional component for your App Component. You can write your CommonComponent logic inside useEffect and then there you can set state for your canRenderBool flag
for example
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Switch, Route, Link, useLocation } from "react-router-dom";
function App() {
const [canRenderBool , setCanRenderBool] = useState(false);
const location = useLocation(); // this will only work if you wrap your App component in a Router
// this effect will run on every route change
useEffect(() => {
// your logic here
// ..
// set value of 'canRenderBool' flag
setCanRenderBool(true);
}, [location]);
return (
// Note that Router is not used here it will be wraped when we export this component, see below
<Switch>
<Route exact path="/">
<Page1 />
</Route>
{canRenderBool && (
<Route exact path="/page-2">
<Page2 />
</Route>
)}
</Switch>
);
}
function Page1() {
return (
<div>
<h1>
Go to <Link to="/page-2">Page 2</Link>
</h1>
</div>
);
}
function Page2() {
return (
<div>
<h1>
Go to <Link to="/">Page 1</Link>
</h1>
</div>
);
}
// Wrapping Router around App and exporting
export default () => (
<Router>
<App />
</Router>
);
{canRenderBool && (
<Router>
<div class="container">
<Switch>
<Route exact path="/comp1"> <Comp1 /> </Route>
<Route exact path="/comp2"> <Comp2 /> </Route>
</Switch>
<Link to="/comp1">Component 1</Link>
<Link to="/comp2">Component 2</Link>
</div>
</Router>
)
}
This might help. And you will need to import all keywords and components before using them.

Routing inside subcomponents not working while using 'exact' : ReactJs

I have created one simple project with 3 main components/pages.
Home page (path: "/") with is login page. once we click on login button, will create a dynamic URL "admin/random_num
LogIn.js
import { Link, Route } from "react-router-dom";
import Dashboard from "../Admin/Dashboard";
export default function Login(props) {
return (
<div>
<h1>Login form</h1>
<Link to={`/admin/${Math.random()}`}>Login Button</Link>
<Route path="/admin/:aid" component={Dashboard} />
</div>
);
}
2nd component/page is admin(path: "/admin/randum_num" , eg: "/admin/132"), which should be displayed when clicked on login button on login page.
Admin.js
export default function Dashboard(props) {
return (
<div>
<h1>Admin Dashboard</h1>
</div>
)
}
3rd component is 404page, which will simply display when no route is matched.
Error404.js
export default function Error404() {
return (
<div>
<h1>404</h1>
</div>
)
}
last not the least, in App.js i am setting up routing.
App.js
import React from 'react'
import './App.css';
import Error404 from './pages/Error404';
import Login from './pages/StartingScreen/Login';
function App() {
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route exact path="/" component={Login} />
<Route component={Error404} />
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
Note: i am able to display the admin dashboard page if i remove "exact" parameter in Route statement but after removing "exact" both Login and Dashboard page will be visible which i dont want. I want only one component should be visible at given route.
I am struggling to find an answer regarding this problem since one week, kindly help. If the given detail is incomplete, let me know.
Since you are using exact for your login component, that means your Login component will only render for the root path /.
And your Dashboard component is inside your Login component, that means you also need to add "/admin/:aid" to your login route.
You need to change this
<Route exact path="/" component={Login} />
to
<Route exact path={["/", "/admin/:aid"]} component={Login} />
Update:
To hide login button, you can use another Switch in your Login component
export default function Login(props) {
return (
<div>
<Switch>
<Route exact path="/">
<h1>Login form</h1>
<Link to={`/admin/${Math.random()}`}>Login Button</Link>
</Route>
<Route exact path="/admin/:aid" component={Dashboard} />
</Switch>
</div>
);
}

React Router different layout each page

I have layout that header and footer components named as MainLayout.
This layout used in Home page.
I want to make another layout that named SubLayout.
This is not have header component, and used in About page.
How to make 2 different layout?
This is what I tried so far below.
Router.js
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import MainLayout from '../components/layouts/MainLayout'
import Home from '../views/home/Home'
import About from '../views/about/About'
export default function Router1() {
return (
<Router>
<Switch>
<Route>
<MainLayout>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
</Switch>
</MainLayout>
</Route>
</Switch>
</Router>
)
}
MainLayout
import React from 'react'
import Header from '../../components/layouts/Header'
import Footer from '../../components/layouts/Footer'
export default function Layout({ children }) {
return (
<div className="wrapper">
<Header />
<div className="container">
<div className="content">
{children}
</div>
</div>
<Footer />
</div>
)
}
SubLayout.js
import React from 'react'
import Footer from '../../components/layouts/Footer'
export default function Layout({ children }) {
return (
<div className="wrapper">
<div className="container">
<div className="content">
{children}
</div>
</div>
<Footer />
</div>
)
}
This is not a trivial problem and happens to a lot of website, especially after you have quite a bit pages.
Solution 1
Coding the layout in MainLayout is difficult since you basically have to fix every possible case of this website from now on, which is a challenge.
In order to avoid that, you can have a separate layout component for each page. ex.
export default function Page1() {
return (
<Header />
<YourPage1Content />
)
}
Although this is a bit extra work for each page, it's very flexible. And the solution is highly scalable no matter how many pages (or features, or sites) that you want to support. For example, you can even wire with entirely different header if you want for a particular page.
Solution 2
If you are looking for a generic way of solving this problem and still want to have a MainLayout since this is the wrapper for every pages, it can be done via Context.
export default function MainLayout() {
const { hideHeader } = useContext(LayoutContext)
return (
<>
{!hideHeader && <Header />}
{children}
</>
)
The context will be provided to you via route, or any custom Context.Provider which can be setup in your App.js or index.js
For instance for a particular route,
export default function MainLayout() {
const location = useLocation()
const hideHeader = location.pathname === '/'
return (
<>
{!hideHeader && <Header />}
{children}
</>
)
This approach is quite generic and highly scalable as well, as long as the same context is used for the entire site.
Summary
If you don't know what site you are building, use solution 1, it'll meet any requirement along the way. However if you have a specific need to meet, solution 2 is really pretty to make sure you can live with a generic Layout component.
All in all, it's about how many Layout component you'd like to reuse.
You can wrap either your whole component with the specific layout or you can wrap the route with specific layout to differentiate like,
export default function Router1() {
return (
<Router>
<Switch>
<MainLayout>
<Route path='/' exact component={Home} />
</MainLayout>
<SubLayout>
<Route path='/about' component={About} />
</SubLayout>
</Switch>
</Router>
)}
And there are unnecessary switch and route statements, cleaned it up a little.

How to reload component when URL is changed in react router

In my React Application I need to reload the component when it comes to the path path="/". I know react-router helps us reload easily the current component but I really need this in my application for some styling purpose. In my application I have two paths:
<Route path="/" component={newRoute}/>
and <Route path="/gallery" component={GalleryImages}/>. So, whenever I move from GalleryImages to newRoute I need to reload the newRoute components otherwise the styles are not working. What is the way around here? Here's myApp.js now:
const newRoute = () => {
return (
<div id="colorlib-page">
<div id="container-wrap">
<div id="colorlib-main">
<Introduction/>
<About/>
<Projects/>
<Timeline/>
<Blog/>
</div>
</div>
</div>
)
}
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Sidebar/>
<Switch>
<Route path="/" component={newRoute} exact/>
<Route path="/gallery" component={GalleryImages} exact/>
<Route component={Error}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
export default App;
Try to use class component instead of functional component

React router - <Route /> does not render when <Link> not in same component

im trying to format my HTML like so within the body tag:
<header id="header"></header>
<main id="app"></main>
<footer id="footer"></footer>
reason why is so that i have my navigation out of <main></main> and in <header></header>
Im also rendering the corresponding React component individually i.e: document.getElementById("header"), document.getElementById("app") ...:
ReactDOM.render(
<Header />,
document.getElementById("header")
);
When clicking <Link to="/log-in"></Link> in <Header /> it breaks out of SPA and jumps to /log-in page.
What am i missing here?
Using ReactDOM.render multiple times will create separate instances unaware of each other source.
Let's go on about restructuring that a bit to make your app feel better:
App.js
import React from 'react';
import { Main } from './components';
const App = () => (
<Main />
)
ReactDOM.render(<App />, document.getElementById("app"));
Main.js
import React from 'react';
import { Router, Route } from 'react-router-dom';
import { Header, Login, Register, Home } from './components'
const Main = () => (
<Router>
<React.Fragment>
<Header />
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Footer />
</React.Fragment>
</Router>
)
export { Main };
So this way, we're ever only really rendering one instance. Your header/footer would be placed outside of the router, so whenever the route changes, they remain unaffected. Keep in mind that this will present a challenge, eg if you want your Header to highlight which route is active, the simples way, since it's outside of router and doesn't receive props from it, is to check the url. Also note that we're using <React.Fragment> here, but you're free to use a <div> or anything else you like. Router expect one child only, so if you don't want additional html elements, you can use a fragment!

Resources