Nesting a BrowserRouter inside another BrowserRouter - javascript

On the following Stackblitz:
https://stackblitz.com/edit/react-testing-routes-navigate-outside-kszb5l?file=src%2Fpages%2FPage2.jsx
I'm trying to include a:
BrowserRouter > Switch > Route > Component
inside another:
BrowserRouter > Switch > Route > Component.
Here is the code for: Page2.jsx:
import React from "react";
import { Link } from "react-router-dom";
import { BrowserRouter, Route, Switch } from "react-router-dom";
const Brush = ({color}) => {
return (
<div>
<div>Brush: {color}</div>
<div>
<Link to="/p2x">/p2x</Link><br />
<Link to="/p2y">/p2y</Link><br />
<Link to="/p2z">/p2z</Link><br />
</div>
</div>
);
};
class Page2 extends React.Component {
constructor(props) {
super(props);
this.state = {
letter: "B"
};
}
render() {
return (
<>
<div>You are currently on: Page 2 (Letter: {this.state.letter})</div>
<Link to="/">Page 1</Link><br />
<br />
<Link to="/page2/p2x">/page2/p2x</Link><br />
<Link to="/page2/p2y">/page2/p2y</Link><br />
<Link to="/page2/p2z">/page2/p2z</Link><br />
<br />
<Link to="/page3">Page 3</Link><br />
<BrowserRouter>
<Switch>
<Route path="/p2x" component={Brush} color="Red" />
<Route path="/p2y" component={Brush} color="Green" />
<Route path="/p2z" component={Brush} color="Blue" />
</Switch>
</BrowserRouter>
</>
);
}
}
export default Page2;
I would expect the brush component to be rendered somewhere on the red line.
Then, I would also expect to have also some other links there that allow me to internally browse between links:
{ /p2x, /p2y, /p2z }
Instead, I get nothing there.
Any idea on how to do that?
My goal is: Create some kind of widget with a few screens I can navigate from one to another. Then I was thinking of having a BrowserRouter in it. Later I could create a library with it that I could import into multiple projects. Do you think about any other way to do that?
Thanks!

In app.js file, you can define route in this way.
<Switch>
<Route exact path="/" component={Page1 } />
<Route path="/page2/p2x" component={Page2 } />
<Route path="/page2/p2y" component={Page2 } />
<Route path="/page2/p2z" component={Page2 } />
<Route path="/page3" component={Page3 } />
</Switch>
Then, in Page2 component, you can render Brush component with parameter from route.

Related

How do I make Portal be a single page all on it own

Sorry, extreme React newbie. I have a simple react function component:
import React from "react";
export default function Portal() {
return (
<h2>Portal</h2>
);
}
In my App.js I have:
import React from 'react';
import { BrowserRouter, Route, Switch } from "react-router-dom";
import './App.css';
import Portal from "./components/Portal/portal";
import LogoHeader from './components/LogoHeader';
import NewFooter from "./components/NewFooter";
function App() {
return (
<div className="App">
<BrowserRouter>
<Switch>
<Route path="/portal">
<Portal />
</Route>
</Switch>
</BrowserRouter>
...
<LogoHeader />
...
<NewFooter/>
</div>
);
}
export default App;
In LogoHeader I have a link:
<a href='/Portal'>Portal</a>
When I click the "Portal" link it refreshes the page, but it now displays the "Portal" header at the top and then the rest of the App.js page. How do I make Portal be a single page all on it own?
UPDATE
Based on an Answer given by #DrewReese I have changed App.js Render to:
return (
<div className="App">
<BrowserRouter>
<p className="greeting">Hello, {token.firstName}</p>
<LogoHeader />
<GetCategories />
<Navbar id="customNav" navItems={navItems} shopCategories={shopCategories} />
<Switch>
<Route path="/home">
<Slideshow id="slideshow" />
<div id="productContainer">
<br />
<h3>Featured Products</h3>
<br />
<FeaturedCards />
<br />
<h3>Most Popular</h3>
<br />
<ProdCard />
<br />
<h3>New Products</h3>
<br />
<ProdCard />
</div>
</Route>
<Route path="/portal">
<Portal />
</Route>
</Switch>
<NewFooter />
</BrowserRouter>
</div>
);
When you use an anchor tag (<a href="...." />) it will trigger a page reload, which reloads your React app. You should use the Link component to link to pages within your app.
LogoHeader
import { Link } from 'react-router-dom';
...
<Link to='/portal'>Portal</Link>
If you want the header to display rendered pages then move it up in the JSX. Remember, any links you render need to be rendered within a routing context, so the header should be rendered within the BrowserRouter component.
App
function App() {
return (
<div className="App">
<BrowserRouter>
<LogoHeader />
<Switch>
<Route path="/portal">
<Portal />
</Route>
</Switch>
<NewFooter/>
</BrowserRouter>
</div>
);
}
If you want the Portal page to be rendered and the header not to be rendered, then you can render a custom header and conditionally render LogoHeader based on route matching.
useRouteMatch
matchPath props
It returns null when provided pathname does not match path prop.
const Header = () => {
const match = useRouteMatch("/portal");
return !match ? <LogoHeader /> : null;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
<Switch>
<Route path="/portal">
<Portal />
</Route>
</Switch>
<NewFooter/>
</BrowserRouter>
</div>
);
}
Update
Within the Switch component, path order and specificity matter. Order more specific paths before less specific paths. "/portal" is more specific than "/" so it should be rendered earlier. This is because the Switch component exclusively matches and renders routes (i.e. only the first match found), as opposed to the Router that inclusively renders them (i.e. all matches).
return (
<div className="App">
<BrowserRouter>
<p className="greeting">Hello, {token.firstName}</p>
<LogoHeader />
<GetCategories />
<Navbar id="customNav" navItems={navItems} shopCategories={shopCategories} />
<Switch>
<Route path="/portal">
<Portal />
</Route>
<Route path="/">
<Slideshow id="slideshow" />
<div id="productContainer">
<br />
<h3>Featured Products</h3>
<br />
<FeaturedCards />
<br />
<h3>Most Popular</h3>
<br />
<ProdCard />
<br />
<h3>New Products</h3>
<br />
<ProdCard />
</div>
</Route>
</Switch>
<NewFooter />
</BrowserRouter>
</div>
);
I suppose you just need to use <Link to="/portal"> from react-router here.
import { Link } from 'react-router-dom' (v4 of react-router)
import { Link } from 'react-router' (v3 of react-router)
There are 2 issues with using a tag:
it does contains relative urls (for this particular case it won't be a problem, but it confuse you on some pages).
it triggers page refresh
More info about using Link
https://reactrouter.com/web/guides/quick-start

React Router - Why is the switch not changing html when the path changes?

I am working on a web app, and i am having some issues with reactJS Router. When i redirect to the /sell path the HTML form the / path stays, and the HTML inside the /sell route doesn't load.
Am i doing something wrong? would anyone be able to point me towards the right direction?
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Helmet from "react-helmet";
import CreateList from "./CreateList";
//https://colorhunt.co/palette/33990
//https://colorhunt.co/palette/66990
class Home extends Component {
constructor(props) {
super(props);
this.state = { appTitle: "better craigslist"};
}
render() {
return (
<React.Fragment>
<Helmet>
<title>{this.state.appTitle}</title>
</Helmet>
<Router>
<Switch>
<Route path="/">
<div id="header-text">
<h1 className="center-align">
<b>Sellify</b>
</h1>
<h5 className="center-align">
Need to get rid of your stuff? create a listing and sell it here! <br /> Looking for something? Check if its on selify!
</h5>
</div>
<div className="col s12 center">
<Router>
<Link to="/sell">
<button className="btn" id="create-listing-button">
Create Listing
</button>
</Link>
</Router>
</div>
</Route>
<Route path="/sell">
<h1>Test</h1>
</Route>
</Switch>
</Router>
</React.Fragment>
);
}
}
export default Home;
Thank you very much!
Issue
You've more than one Router. The inner Router context is handling the links so the outer Router isn't notified.
Solution
Use only one Router component, remove the Router around the Link. Additionally, when using a Switch, path order and specificity matter. You want to oder your more specific paths before less specific paths. "/" is a path prefix for all paths and would be matched and rendered before "/sell".
class Home extends Component {
constructor(props) {
super(props);
this.state = { appTitle: "better craigslist" };
}
render() {
return (
<React.Fragment>
<Helmet>
<title>{this.state.appTitle}</title>
</Helmet>
<Router>
<Switch>
<Route path="/sell">
<h1>Test</h1>
</Route>
<Route path="/">
...
<div className="col s12 center">
<Link to="/sell">
<button className="btn" id="create-listing-button">
Create Listing
</button>
</Link>
</div>
</Route>
</Switch>
</Router>
</React.Fragment>
);
}
}

How to remove navbar from certain pages in react

I am building a website and I don't want to display the navbar in 2 pages. One is the 404 page where I will be giving a redirect button. The other is the landing page of the website where I will be a giving a button which would redirect to the home page of the website. This is my app.js code where I have my routes defined.
import React from "react";
import "./App.css";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Navbar from "./Components/Navbar";
import Home from "./Components/Pages/Home";
import PlanYourTrip from "./Components/Pages/PlanYourTrip";
import AboutUs from "./Components/Pages/AboutUs";
import SafetyMeasures from "./Components/Pages/SafetyMeasures";
import Travel from "./Components/Pages/Travel";
import Error from "./Components/Pages/404";
function App() {
return (
<>
<Router>
<Navbar />
<Switch>
<Route path="/" exact component={Travel} />
<Route path="/home" exact component={Home} />
<Route path="/plan-your-trip" exact component={PlanYourTrip} />
<Route path="/about-us" exact component={AboutUs} />
<Route path="/safety-measures" exact component={SafetyMeasures} />
<Route component={Error} />
</Switch>
</Router>
</>
);
}
export default App;
I want to remove the navbar from <Route path="/" exact component={Travel} /> and <Route component={Error} /> . Please help.
<Navbar /> needs to check to window.location and render empty
See https://reactrouter.com/web/api/Hooks/uselocation
Or create a new component that does the check and render it children
<MayRenderNav><Navbar /></MayRenderNav>
The problem with your code is that <Navbar /> component will load without caring about the route.
You can simply put your <Navbar /> component inside the components you want it to load in and simply leave for others.
To add more context on uke answer you can use the useLocation hook inside your navbar an do something like this
// All the routes you want to exclude
const withouSidebarRoutes = ["/about"];
function Navbar() {
const {pathname} = useLocation();
// Validates if the current pathname includes one the routes you want to hide the sidebar is present on the current url
// If that's true render null instead of the sidebar
if (withouSidebarRoutes.some((item) => pathname.includes(item))) return null;
return (
//your navbar code.
)
}
The includes is useful because if you have nested routes like about/1 it will exclude that one too, use a simple equal if you just want to exclude the about page and not the nested ones.
withouSidebarRoutes.some((item) => pathname === item)
Check the hooks api reference to see what the useLocation can do: https://reactrouter.com/web/api/Hooks/uselocation
Also I have a working sandbox with a sidebar that hides when you're in the about section.
https://codesandbox.io/s/cranky-lewin-p8ozv
This might feel a bit cheat, but it does the trick.
To hide navbar from homepage (path = "/") is quite straight-forward. We can do by the book, use render in "Route".
The tricky part is how to hide from 404 Error page, which doesn't match all other route in your website.
The trick I used is to call useEffect on Error page mount, change style of navbar to display: none;
import React from "react";
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
import "./styles.css";
const NavBar = () => {
return (
<div className="navbar">
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
<Link to="/error">Error</Link>
</div>
);
};
const Home = () => (
<div>
<h1>This is home</h1>
<ul>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</div>
);
const About = () => <div>About</div>;
const Contact = () => <div>Contact</div>;
const Error = () => {
React.useEffect(() => {
document.getElementsByClassName("navbar")[0].style.display = "none";
}, []);
return (
<div>
<h1>Error</h1>
<Link to="/">Back to home</Link>
</div>
);
};
export default function App() {
return (
<Router>
<Route
render={({ location }) => {
if (location.pathname !== "/") return <NavBar />;
}}
/>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Route component={Error} />
</Switch>
</Router>
);
}
body {
margin: 0;
padding: 0;
}
.App {
font-family: sans-serif;
text-align: center;
}
.navbar {
background: black;
color: white;
padding: 10px 20px;
}
.navbar a {
display: inline-block;
padding: 6px 12px;
text-decoration: none;
color: white;
text-transform: uppercase;
}
Link to sandbox

react router dom library doesn't work for my project

my name Rajdeep Singh. I'm using react-router-dom in my react project. when clicking on the Link tag, Link tag not work.im don't understand why not working.
in-browser change URL but a component, not change on the web app
check my code
This my Main.js Component file code
import React, { Fragment } from 'react';
import Foot from './Footer';
import Head from './Header'
import MainContect from './MainContect';
import About from './page/About';
import Book from './page/Book'
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
function Main() {
return (
// use Fragment
<Fragment>
{/* use BrowserRouter as Router */}
<Router>
{/* out Router use Head componet */}
<Head />
{/* use Switch tag */}
<Switch>
{/* use Route defined Router path */}
<Route path="/" exact >
{/* compnent name */}
<MainContect />
</Route>
<Route path="/book" >
<Book />
</Route>
<Route path="/about" >
<About />
</Route>
</Switch>
{/* out Router use Head componet */}
<Foot />
</Router>
</Fragment>
)
}
export default Main
This my Header.js component file code
import React from 'react';
import './css/header.scss';
import { BrowserRouter as Router, Link } from 'react-router-dom';
function Head() {
return (
// Use Router tag wrap all Link in Router
<Router>
<header>
<h1 className='logo'> Sikh Book</h1>
<div className="iconArea">+</div>
<ul className="ulArea">
<li>
{/* Use Link tag for navigation */}
<Link to="/"> Home </Link> </li>
<li>
<Link to="/book">
Book
</Link>
</li>
<li>
<Link to="/about">
About
</Link>
</li>
</ul>
</header>
</Router>
)
}
export default Head
Plz tell whats my mistake
thank for helping me
You should initiate the BrowserRouter only once. I'd do this at a high level of your component tree. You could look at this example.
// BrowserRouter is the router implementation for HTML5 browsers (vs Native).
// Link is your replacement for anchor tags.
// Route is the conditionally shown component based on matching a path to a URL.
// Switch returns only the first matching route rather than all matching routes.
import {
BrowserRouter as Router,
Link,
Route,
Switch,
} from 'react-router-dom';
import React from 'react';
const Home = () => <h1>Home</h1>;
const About = () => <h1>About</h1>;
// We give each route either a target `component`, or we can send functions in `render` or `children`
// that return valid nodes. `children` always returns the given node whether there is a match or not.
const App = () => (
<Router>
<div>
<Link to="/">Home</Link>{' '}
<Link to={{pathname: '/about'}}>About</Link>{' '}
<Link to="/contact">Contact</Link>
<Switch>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
<Route
path="/contact"
render={() => <h1>Contact Us</h1>} />
<Route path="/blog" children={({match}) => (
<li className={match ? 'active' : ''}>
<Link to="/blog">Blog</Link>
</li>)} />
<Route render={() => <h1>Page not found</h1>} />
</Switch>
</div>
</Router>
);
Credits to: siakaramalegos

React-router v4 updates url but doesn't render component

I'm using react-router v4, no redux. The code example is below:
class MainPage extends Component {
render() {
return (
<div className="MainPage">
<BrowserRouter>
<Switch>
{/* <Route exact path='/' /> */}
<Route path='/signin' component={SignIn}/>
<Route path='/signup' component={SignUp} />
<Redirect from='*' to='/' />
</Switch>
</BrowserRouter>
</div>
);
}
}
When I'm using Link it updates URL in browser but doesn't render anything, nothing happens. When I resfresh, everything becomes fine and component renderes;
export default class Navbar extends Component {
render() {
return (
<div className="navbar">
<BrowserRouter>
<div>
<Link to='/signin'>Sign in</Link>
<Link to='/signup'>Sign up</Link>
</div>
</BrowserRouter>
</div>
);
}
}
I already tried everything, even withRouter(Component), but it says that with router may only be used inside
How can I deal with this?
Here is the working code. As others explained you should use one BrowserRouter. If you want to render your Navbar component all the time then you should place it above Switch but under BrowserRouter hence you need Link there.
const Navbar = () => (
<div className="navbar">
<Link to='/signin'>Sign in</Link>
<Link to='/signup'>Sign up</Link>
</div>
);
class MainPage extends React.Component {
render() {
return (
<div className="MainPage">
<BrowserRouter>
<div>
<Navbar />
<Switch>
{/* <Route exact path='/' /> */}
<Route path='/signin' component={SignIn} />
<Route path='/signup' component={SignUp} />
<Redirect from='*' to='/' />
</Switch>
</div>
</BrowserRouter>
</div>
);
}
}
You should only have a single BrowserRouter component in your tree. The BrowserRouter component holds the shared state the router used to synchronize the URL with the rendered routes. In your situation, you are getting two different versions of router state because you rendering two BrowserRouter components so you should probably render a single BrowserRouter component somewhere higher in your component tree.
If you have an App component that renders both Navbar and MainPage then you can move the router into that component:
export default class App extends Component {
render() {
return (
<BrowserRouter>
<div className="AppContainer">
<Navbar />
<MainPage />
</div>
</BrowserRouter>
);
}
}

Categories

Resources