3

I want to show or hide NewsLetter according to the current path. For example I want it hidden for login and register pages, i.e., (path login & register). I have tried using useLocation but it cannot be used outside of the router.

I hope to get a good solution so that I can also render navbar and footer in similar conditions too. Here is my current code inside App.js:

<BrowserRouter>
  <Wrapper>
    <Announcement />
    <Navbar />
    <Routes>
      <Route index path="/" element={<Home />}></Route>
      <Route path="/products" element={<ProductList />}></Route>
      <Route path="/products/:category" element={<ProductList category="true" />}></Route>
      <Route path="/product/:id" element={<Product />}></Route>
      <Route path="/register" element={isAuthenticated ? <Navigate replace to="/" /> : <Register />}></Route>
      <Route path="/login" element={isAuthenticated ? <Navigate replace to="/" /> : <Login />}></Route>
      <Route path="/account" element={isAuthenticated ? <Navigate replace to="/" /> : <Account />}></Route>
      <Route path="/forgot-password" element={isAuthenticated ? <Navigate replace to="/" /> : <ForgotPassword />}></Route>
      <Route path="/cart" element={<Cart />}></Route>
      <Route path="/wishlist" element={<Wishlist />}></Route>
      <Route path="/orders/tracking" element={<Tracking />}></Route>
      <Route path="/orders/checkout" element={<Checkout />}></Route>
    </Routes>
    <Newsletter />
  </Wrapper>
  <Footer />
</BrowserRouter>
Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65
felixkpt
  • 128
  • 3
  • 10

3 Answers3

2

First solution

You could simply leave Newsletter component where it's inside BrowserRouter and change its content as below. Using useLocation either it renders its content or return an empty JSX depending on the path.

import { useLocation } from "react-router-dom";
const Newsletter = () => {
  // ...
  const { pathname } = useLocation();
  // ...
  if (pathname === "/login" || pathname === "/register") {
    return <></>;
  }
  // render actual content
  return <div></div>;
};
export default Newsletter;

Second one

Another way is to control everything in one place by creating a Layout.js commponent, like so:

import { Outlet, useLocation } from "react-router-dom";

const Layout = () => {
  const { pathname } = useLocation();
  return (
    <>
      <Announcement />
      <Navbar />
      <Outlet />
      {pathname !== "/login" && pathname !== "/register" && <Newsletter />}
    </>
  );
};

export default Layout;

Then change your routes set up as below. Notice the Layout component. Make sure to import it:

// ⚠️  Other imports
import Layout from "./components/Layout"; // ⚠️ Verify it's the correct path

export default function App() {
  return (
    <>
      <BrowserRouter>
        <Wrapper>
          <Routes>
            <Route element={<Layout />}>
              <Route index path="/" element={<Home />}></Route>
              <Route path="/products" element={<ProductList />}></Route>
              {/* Other routes... */}
            </Route>
          </Routes>
        </Wrapper>
        <Footer />
      </BrowserRouter>
    </>
  );
}

You can have as many layouts as you want (MainLayout, AuthLayout, Layout...). The setup is the same, an Outlet and a Route that has your Layout as element and wraps the pages for that Layout.

Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65
  • 1
    This works! Meaning I will have to write the same code for checking the condition again inside the other components like the Navbar and Footer, that I would want to render according to the same rule? – felixkpt Aug 27 '22 at 11:14
  • Yeah that's it, each component knows where it should render. – Youssouf Oumar Aug 27 '22 at 11:18
  • 1
    Thanks a lot, I have decided to stick with this solution for now. – felixkpt Aug 27 '22 at 11:22
1

Maybe try with nested route by using Outlet in Newsletter component

<BrowserRouter>
 <Wrapper>
   <Announcement />
   <Navbar />
   <Routes >
       <Route exact path="/" element={<Newsletter />} >
           <Route index element={<Home />}></Route>
           <Route path="products" element={<ProductList />}></Route>
           <Route path="products/:category" element={<ProductList category="true" />}></Route>
           <Route path="product/:id" element={<Product />}></Route>
           <Route path="register" element={isAuthenticated ? <Navigate replace to="/" /> : <Register />}></Route>
           <Route path="login" element={isAuthenticated ? <Navigate replace to="/" /> : <Login />}></Route>
           <Route path="account" element={isAuthenticated ? <Navigate replace to="/" /> : <Account />}></Route>
           <Route path="forgot-password" element={isAuthenticated ? <Navigate replace to="/" /> : <ForgotPassword />}></Route>
           <Route path="cart" element={<Cart />}></Route>
           <Route path="wishlist" element={<Wishlist />}></Route>
           <Route path="orders/tracking" element={<Tracking />}></Route>
           <Route path="orders/checkout" element={<Checkout />}></Route>
       </Route>
   </Routes>
 </Wrapper>
 <Footer />
</BrowserRouter>

and in Newsletter component

const Newsletter = () => {
    ... your conditions 

    return (
        <Outlet /> 
        ... newsletter content
    )
}
iPramod
  • 109
  • 4
-1

May be you are looking for this :

window.location.pathname
  • 1
    I have tried using window.location.pathname but I is not dynamic in that I can't change when the path gets updated – felixkpt Aug 27 '22 at 08:31
  • don't store it in a different variable just use it directly it will get updated each time your route changes. – Abhishek Mittal Aug 27 '22 at 08:35
  • 1
    I have tried your solution, working but not dynamic in that you have to reload the page to effect the changes – felixkpt Aug 27 '22 at 08:39
  • @AbhishekMittal please provide a solution based on what OP questions. Your solution is just to get the current pathname and the problem is the window.location.pathname doesn't change when the route is changed. – Fai Zal Dong Mar 27 '23 at 00:50