How to hide certain Components for some Routes [duplicate] - reactjs

This question already has answers here:
How do I render components with different layouts/elements using react-router-dom v6
(2 answers)
Closed 10 months ago.
I want to hide Header and Footer Component in some routes, e;g for path="/invoice/:id/print"
I have an app with this type of layout using react-router-dom v5
<Router>
<Header />
<main className="py-0 px-0">
<div>
<Container fluid>
<Switch>
<Route path="/" component={HomeScreen} exact />
<Route path="/invoices" component={Invoices} exact />
<Route path="/invoice/:id/print" component={InvoicePrint} exact />
<Route path="/billing/invoice/create" component={InvoiceCreate} exact />
<Route path="*" exact>
<Error404 />
</Route>
</Switch>
</Container>
</div>
</main>
<Footer />
</Router>
The problem is if I go to
<Route path="/invoice/:id/print" component={InvoicePrint} exact />
Then Header and Footer also get rendered. But I want to hide them for this specific route. So how can I do that?
I'm using react-router version 5

That depends on how many pages should render the Header and Footer components.
If you want to render them on just a few pages, the simplest solution will be to move them to the components/pages where you'd like to render them.
If you'd like to hide them in just one or two places you can use the useRouteMatch hook.
You can also build some abstractions to simplify the readability a bit: let's make the Layout component, which will accept a prop (like renderHeaderAndFooter (you can change the name of course 😄)).
Layout component:
const Layout = ({ renderHeaderAndFooter, children }) => (
<div>
{renderHeaderAndFooter && (
<Header />
)}
{children}
{renderHeaderAndFooter && (
<Footer />
)}
</div>
)
and the place of usage:
const HomeScreen = () => (
<Layout renderHeaderAndFooter={true}>
// make stuff
</Layout>
)
const Invoices = () => (
<Layout renderHeaderAndFooter={false}>
// make stuff
</Layout>
)

simple way is to trigger your current route by using useRouteMatch in every component you want like:
const match = useRouteMatch("/invoice/:id/print"); // import { useRouteMatch } from "react-router-dom";
return !!match ? <></> : <Footer/> //or Header
check this

You have to update the Header and Footer components by adding a listener during the rendering phase of the component : componentDidMount()
Fetch the called component :
const shouldHide = useRouteMatch("/invoice/:id/print") || useRouteMatch("/other/route");
return (
!shoudlHide && <Header />
)

Related

React router v6 nested routes can't go up one level using navigate

Target
I'm using react router v6.
I have a parent route with a static header with a back button that when I click it needs to go one path above, exp. /shop/1/item/create to /shop/1/item, when i click the back button i call the function navigate from useNavigate()
example
Red is the root page, yellow is where the static header is and green is content that i want to change
Problem
When i call navigate regardless of using "." or ".." or "./" the page never change correctly, it either redirects from /shop/1/items/create to /shop or to /shop/1
the only way of redirecting correctly is using -1 but it causes the problem that if someone copy's the URL when going back they get redirected to whatever page they were before pasting the URL.
Code
const ShopPage = () => {
const [ state, setState ] = React.useState<IShopPageState>({ shop: { name: "" }, isInvalid: false })
const { id } = useParams()
const navigate = useNavigate()
return (
<div id="shop-page">
<Card>
<div id="shop-page-header">
<Icon canHover onClick={() => navigate("./")} icon="BiArrowBack"/>
<Title text={state.shop ? state.shop.name : ""}/>
</div>
</Card>
<div id="shop-page-content">
<Routes>
<Route path="/*" element={<div onClick={() => navigate('./items')}>wddw</div>}/>
<Route path="/items/*" element={<ItemsPage shop={state.shop}/>}/>
<Route path="/items/create" element={<ItemPage/>}/>
</Routes>
</div>
</div>
)
}
Here is the code of the yellow part with only the important stuff, the header is static and always visible and where the back button is.
I believe this code is enough to understand, the green part only redirects the url from /items/ to /items/create using navigate('./create')
I think a possible solution would be simply copying pasting the header for each page but i find that to be bad practice and it is a last resort solution
Here is a example of the problem
EDIT
As asked here is some extra code showing the problem
App.js
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="*" element={<Link to="/shop">Shop</Link>} />
<Route path="shop/*" element={<Shop />} />
</Routes>
</BrowserRouter>
);
}
Shop.js
const Shop = () => {
return (
<>
<div>
<Link to="./">Back</Link>
</div>
<Routes>
<Route path="*" element={<Link to="./items">All</Link>} />
<Route path="items/*" element={<Link to="./create">Item</Link>} />
<Route path="items/create" element={<>Create</>} />
</Routes>
</>
);
};
if you are using a nested route then better to use
<Route path="/" >
<Route index element={<Items/>} />
<Route path="item:id" element={<Item/>} />
</Route>
Hey Dear #pekira you can see just a simple hint in the code below for React Router v6
import { Routes, Route, Link } from 'react-router-dom';
const App = () => {
return (
<>
<h1>React Router</h1>
<nav>
<Link to="/home">Home</Link>
<Link to="/user">User</Link>
</nav>
<Routes>
<Route index element={<Home />} />
<Route path="home" element={<Home />} />
<Route path="user/:Id" element={<User />} />
<Route path="*" element={<NoMatch />} />
</Routes>
</>
);
};

How to hide a component in all nested routes with React router dom V6?

I'd like to hide a component depending on the route with react-router-dom v6. I could make it with exact path, but I'd also like to hide it in nested routes. I tried like this:
{location !== "/dashboard/*" && <Header />}
But it works only on the page http://localhost:3000/dashboard/*, which is not what I want. How could I fix this?
This is the full code:
const location = useLocation().pathname
const conditionLocation = location === "/dashboard" || location === "/dashboard/*"
return !conditionLocation && <Header />
Routes:
// Packages
import React from "react"
import { Routes, Route } from "react-router-dom"
// User
import Dashboard from "../pages/admin/Dashboard"
import EditAccount from "../pages/admin/EditAccount"
function Switch() {
return (
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/dashboard/edit" element={<EditAccount />} />
</Routes>
)
}
export default Switch
Thanks for your help!
Fix this issue using javascript regex:-
{!(location.match(/^\/dashboard.*$/gim)) && <Header />}
Hope that works..
From what I understand, you are wanting to render a Header component only on specific routes. You've not shared much context, but I understand you don't want to render the Header on any "/dashboard" path. You can achieve this with layout and Outlet components.
const HeaderLayout = () => (
<>
<Header />
<Outlet /> // <-- wrapped Route components render into outlet
</>
);
For the routes you want to render with the Header component render them into a route rendering the HeaderLayout.
<Routes>
<Route element={<HeaderLayout />}>
..... routes render into outlet with header
</Route>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/dashboard/edit" element={<EditAccount />} />
</Routes>

How to prevent a React component from unmounting when url path is matched using react-router-dom

I'm currently struggling using React and its library react-router-dom.
To simplify:
I've this in my App component:
const App = () => {
return (
<Router>
<Layout>
<Switch>
// <Other Routes />
<Route path='/product/:id' component={ProductScreen} />
// <Other Routes />
</Switch>
</Layout>
</Router>
)
}
The <ProductScreen /> (single product view) is wrapped like this:
The <ProductNav /> component embeds two links from react-router-dom. It also includes the required logic to reach previous or next product. Like so for instance:
<Link
className='prev'
to={`/product/${currentIndex - 1 < 0 ? ids[lastIndex] : ids[currentIndex - 1]}`}
>
Previous
</Link>
It's working but I'm not satisfied with that approach because the <ProductScreen /> unmounts each time I click 'previous' or 'next' and this leads to some unaesthetic things on the page. I would like to prevent it from unmounting in order to have this instead:
I tried many things, read the doc but I'm quite stuck right now. If anyone has any idea of how to achieve that I would be glad to read it.
You can use Route nested Route as following:
<Header />
<Main>
<Route path="/products" component={Product> />
</Main>
<Footer />
on page Product
const {url} = useRouteMatch();
render() {
<>
<Navbar />
<Route path=`${url}/:id` component={ProductDetail} />
</>
}

Nested route link not working React Router

I built my website on ReactJS to be SPA. I am trying to use React Router v.5.2.0. I have a sample app called ToDoList.js that should be at the URL on the portfolio page is below. For some reason it works fine on my localhost, but not when I deploy it to Netlifly and hosted on my Github. It doesn't seem to work when it's live here. The back buttons on the browser also don't seem to work right either.
Portfolio.js:
<Link to="/portfolio/todo-list">here</Link>
The URL in the browser updates to the right URL, but it doesn't load the component. In my App.js file I have 2 types of routes so the sample app (todo list) doesn't show the top nav. It works
App,js:
function App() {
return (
<BrowserRouter>
<div className="App">
<Switch>
<MainRoute exact path="/" component={Home} />
<MainRoute exact path="/about" component={About} />
<MainRoute exact path="/portfolio" component={Portfolio} />
<MainRoute exact path="/contact" component={Contact} />
<PortfolioRoute exact path="/portfolio/todo-list" component={ToDoList} />
</Switch>
</div>
</BrowserRouter>
);
}
export const MainRoute = ({ component: Component, ...rest}) => {
return(
<Route {...rest} component={(props) => (
<>
<TopNav />
<Container fluid>
<Row className="content-row">
<Component {...props} />
</Row>
</Container>
<Gradient />
</>
)} />
);
};
export const PortfolioRoute = ({ component: Component, ...rest}) => {
return(
<Route {...rest} component={(props) => (
<>
<Component {...props} />
</>
)} />
);
};
export default App;
All the endpoints along the /portfolio route need to be handled in one place, otherwise ReactRouter can't handle the nesting as it selects the Main one first.
See the docs' demo on nesting here
Also, so that directly linking to https://www.aubreyhlaverty.com/portfolio/todo-list works, you should add a 200 redirect to your Netlify _redirects file, like this. Might also help with your other issue.
/* /index.html 200

How to hide Navbar in Login Component with ReactJS?

Recently I started learning React and my problem here is that I cannot hide Navbar when Im in Login page/component. I have a Router in index.js like this:
const routing = (
<div>
<NavBar />
<Router>
<div>
<Switch>
<Route exact path="/" component={App} />
<Route path="/users/:id" component={Users} />
<Route path="/users" component={Users} />
<Route path="/contact" component={Contact} />
<Route path="/login" component={LoginPage} hideNavBar={true} />
<Route component={Notfound} />
</Switch>
</div>
</Router>
</div>
)
ReactDOM.render(routing, document.getElementById('root'));
From the little search that I made most approaches were like inserting <NavBar /> in every component and use a flag to hide it when im in Login. Is there any other way like modifying the above code simple and fast?
Not really you could have restrict access to all other routes like below I suppose
const PrivateRoute = ({component: Component, ...rest }) =>
<Route
{...rest}
render={props =>
authenticated ? <Container ><Component {...props} /> <Container /> : <Redirect to="/login" />
}
/>
};
Where you container has a header , maybe footer and takes a child prop (your component)
There are better approaches for hiding the NavBaron authenticated routes, but if you want to hide it when it's on foo route, you could check the path name and decide to render it or not.
import { useLocation } from "react-router-dom";
const NavBar = () => {
const location = useLocation()
return location.pathname == '/login' ? <YourNavBarComponents /> : null
}
The best way to hide parts of the UI building with React is to not include the markup. In order to do NOT display parts relying on the authentication you should have a flag if the authentication has done and is successful.
For example you can build a component that share some context in order to check if the authentication flag is true and render the parts that must be available only to users who have logged in.
You can follow this example to see the details how to build that component.
Basically you have a component that wraps another component and based on some rules render its children or call a render prop:
function Private({ shouldRender = false, children }) {
return shouldRender
? children
: null;
}
function App() {
return (
<div>
<h2>Application</h2>
<Private><div>This part is hidden</div></Private>
<Private shouldRender={true}><div>This part is <strong>available</strong></div></Private>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Resources