React-Router v4 separating Link and Route path elements - javascript

(Updated for clarity) I have the following structure for my App:
<Layout />
/components/
<Header />
<Navigation />
<Stage />
<Footer />
/pages/
<Home />
<About />
<Contact />
My thinking is to have the <Navigation> element isolated, and imported into the parent <Header> (and later, Site Map). All components are then staged into the parent <Layout>, which is fed into the <div id="root">
The content of my /pages/ Components are successfully called and rendered with the code below. However this page content is rendered inside of my <header> element, breaking the page hierarchy.
My intention is to have the <Switch> device render the /pages/ Components within the <Stage> parent Component.
Any attempt to move the <Switch> element to the <Stage> component causes the whole app to not load/render.
import React from 'react';
import {
BrowserRouter as Router, Route, Link, Switch
} from 'react-router-dom';
import Home from './../pages/Home';
import About from './../pages/About';
import Contact from './../pages/Contact';
export default class Navigation extends React.Component {
render() {
return (
<Router>
<div className="navigation">
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
{/*
Below should be repositioned outside of <header>, in Stage component
*/}
<div className="stage">
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</div>
</div>
</Router>
)
}
}
My question is: How can I have the Router render the required /page/ Components inside my <Stage> component, when selected from the <Navigation> component.
Since all the reacttraining.com examples are single-page, I'm struggling to determine if I'm making a logic mistake in assuming how my app should be optimally constructed, or a programming error in recreating a relation between the two elements with react-router v4.

All you need to do is to separate these two divs as you are wrapping stage inside the header. And as the Component needs to be rendered in a single element you need another div around the whole content:
export default class Navigation extends React.Component {
render() {
return (
<Router>
<div>
<div className="navigation">
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
</div>
<div className="stage">
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</div>
</div>
</Router>
);
}
}
Update:
Updating my answer:
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const Header = () => <h1>I'm a header</h1>;
const Navigation = () => (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
);
const Stage = () => (
<div>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</div>
);
const Footer = () => <h6>Footer</h6>;
const Home = () => <h3>Home</h3>;
const About = () => <h3>About</h3>;
const Contact = () => <h3>Contact</h3>;
const App = () => (
<Router>
<div>
<Header />
<Navigation />
<Stage />
<Footer />
</div>
</Router>
);
render(<App />, document.getElementById('root'));
And here is the working version:
https://codesandbox.io/s/v21BD37NX

Related

React navbar renders components separately with react-router-dom

I am using react router dom to render a website with react and tailwindcss. The navbar when clicked refreshes and renders the component on it's own without the other components. This is not what I want.
I want the components rendered in a single page in the way it is structured in my App.js file
I have tried using normal hrefs and now Link from react-router-dom, But I still get the same result
App.js
const App = () => {
return (
<div className="max-w-[1440px] mx-auto bg-page overflow-hidden relative">
<Header />
<Routes>
<Route path="/" element={<Banner />} />
<Route path="/home" element={<Banner />} />
<Route path="/about" element={<About />} />
<Route path="/hero" element={<Hero />} />
<Route path="/featured" element={<Featured />} />
<Route path="/gallery" element={<GallerySection />} />
<Route path="/testimonies" element={<Testimony />} />
<Route path="/join" element={<Join />} />
<Route path="/footer" element={<Footer />} />
<Route path="/copy" element={<Copyright />} />
</Routes>
</div>
);
};
export default App;
Index.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>
);
Navbar.js
const Nav = () => {
return (
<nav className="hidden lg:flex">
<ul className="flex gap-x-8 text-white">
{["home", "about", "featured", "gallery", "testimonies", "join"].map(
(item) => {
return (
<li key={`link-${item}`}>
<Link to={`/${item}`} className="capitalize">
{item}
</Link>
</li>
);
}
)}
</ul>
</nav>
);
};
export default Nav;
I would appreciate any help please
You need to use hashtags instead different routes.
So you have to use only one index route
<Route path="/" element={<IndexPage />} />
and render section components in IndexPage component one by one
const IndexPage = () => (
<>
<Banner />
...
</>
)
then add into each section component wrapper id attribute
const Banner = () => (
<div id="banner">
...content...
</div>
)
and use hashtag links in you Nav component:
<Link to={`#${item}`} className="capitalize">
{item}
</Link>
links will be looks like domain.com/#banner and if use open it then browser will scroll down to specific element with id
also I suggest to add some css to make scrolling smooth:
html {
scroll-behavior: smooth;
}

How to access BrowserRouter history outside of BrowserRouter?

Below is my code snippet simplified. (using react-router-v5)
My question is how to get access to BrowserRouter's history in the logout_Handler(), given that I am "outside" BrowserRouter?
I've seen this answer How to access history object outside <Route /> in React Router v4
, but there is also a comment which applies to my case:
"When I try to use "withRouter" as shown here I get the error
You should not use <Route> or withRouter() outside a <Router>
which is exactly what I get, if I try to use withRouter at the App.js level.
// App.js
function App(props) {
const logout_Handler = (e) => {
localStorage.removeItem("app-tokenObj");
// how to get the history from BrowserRouter to redirect?!
//history.push("/login") ?? NO IDEA ??
}
return (
<BrowserRouter>
<nav>
<h3>My Supa Dupa App</h3>
<Link to="/">Home Page</Link>
<Link to="/admin">Admin Page</Link>
<button type="button" onClick={logout_Handler}>Logout</button>
</nav>
<hr/>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/link1" component={Comp1} />
<Route path="/link2" component={Comp1} />
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
</Switch>
</BrowserRouter>
)
}
If you are trying to access parts of a routing context in the App component then the router providing the context needs to be higher in the ReactTree. The solution is simple. Move BrowserRouter higher than App in your apps code. Wrapping App in the BrowserRouter would be sufficient enough for this.
Example index.js
ReactDOM.render(
...
<BrowserRouter>
<App />
</BrowserRouter>
...,
root,
);
App
The App component now has a routing context available. While you could use the withRouter Higher Order Component, using the useHistory hook would be simpler.
import { Link, Route, useHistory } from 'react-router-dom';
function App(props) {
const history = useHistory();
const logout_Handler = (e) => {
localStorage.removeItem("app-tokenObj");
history.push("/login");
}
return (
<>
<nav>
<h3>My Supa Dupa App</h3>
<Link to="/">Home Page</Link>
<Link to="/admin">Admin Page</Link>
<button type="button" onClick={logout_Handler}>Logout</button>
</nav>
<hr/>
<Switch>
<Route path="/link1" component={Comp1} />
<Route path="/link2" component={Comp1} />
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
<Route path="/" component={Home} />
</Switch>
</>
);
}
#for move one folder previous:
history.push("/../login")

Error: [Header] is not a <Route> component

I continue to run into the above mentioned error despite my efforts to fix them. The terminal claims the application compiles without issue but nothing shows up on the browser. I found the error my looking at the console. Here is the index.js file for the Header component that the error is referring to:
import React from "react";
import { Route, Navigate, Router } from "react-router-dom";
import Navigation from "../../components/Navigation";
import About from "../../components/About";
import Portfolio from "../../components/Portfolio";
import Contact from '../../components/Contact';
import Resume from '../../components/Resume';
class Header extends React.Component {
render() {
return (
<Router>
<header>
<Navigation />
</header>
<div className="content">
<Route exact path="/" render={() => <Navigate to="/about" replace={true}/>} />
<Route path="/about" component={About} />
<Route path="/portfolio" component={Portfolio} />
<Route path="/contact" component={Contact}/>
<Route path="/resume" component={Resume}/>
</div>
</Router>
);
}
}
export default Header;
Here is the App.js:
import React from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Routes } from 'react-router-dom';
function App() {
return (
<div>
<Routes>
<Header />
<Footer />
</Routes>
</div>
);
}
export default App;
I can provide further code if it is needed.
In react-router-dom version 6 Routes components can have only Route or React.Fragment as a child component, and Route components can have only Routes or another Route component as parent. Header is not a Route component and fails the invariant.
Move the Router into app and render the Header and Footer components into it instead, then wrap the Route components in Routes.
App
function App() {
return (
<div>
<Router>
<Header />
<Footer />
</Router>
</div>
);
}
Header
Fix the Route components, they no longer use component or render to render the routed components, instead they now use an element prop to render ReactElements, i.e. JSX.
class Header extends React.Component {
render() {
return (
<div>
<header>
<Navigation />
</header>
<div className="content">
<Routes>
<Route path="/" element={<Navigate to="/about" replace />} />
<Route path="/about" element={<About />} />
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/contact" element={<Contact />}/>
<Route path="/resume" element={<Resume />}/>
</Routes>
</div>
</div>
);
}
}
Though it may make more sense to move the routes into the app as content between the header and footer.
function App() {
return (
<div>
<Router>
<Header />
<div className="content">
<Routes>
<Route path="/" element={<Navigate to="/about" replace />} />
<Route path="/about" element={<About />} />
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/contact" element={<Contact />}/>
<Route path="/resume" element={<Resume />}/>
</Routes>
</div>
<Footer />
</Router>
</div>
);
}

react-router-dom doesn't work for my project

I installed react-router-dom to switch between navbar elements. The library does not want to cooperate with my project. After clicking on the navbar element I am not redirected to the required component. Sometimes when I click on a selected item the menu moves slightly to the left. My code looks like this:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(
<App />, document.getElementById('root'));
Navbar.js
import React, { useState } from "react";
import App from '../components/App'
import About from '../components/About';
import Services from '../components/Services';
import Contact from '../components/Contact';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
const Navbar = () => {
const [navLinkOpen, navLinkToggle] = useState(false);
const handleNavLinksToggle = () => {
navLinkToggle(!navLinkOpen);
};
const renderClasses = () => {
let classes = "navlinks";
if(navLinkOpen) {
classes += " ' ' + active";
}
return classes;
};
return (
<nav>
<div className="logo">
<h4>Delightartco</h4>
</div>
<ul className={renderClasses()}>
<Router>
<li className="link"><Link to={"/home"}>Home</Link></li>
<li className="link"><Link to={"/about"}>About</Link></li>
<li className="link"><Link to={"/services"}>Services</Link></li>
<li className="link"><Link to={"/contact"}>Contact</Link></li>
<Switch>
<Route path="/home" component={App}>
</Route>
<Route path="/about" component={About}>
</Route>
<Route path="/services" component={Services}>
</Route>
<Route path="/contact" component={Contact}>
</Route>
</Switch>
</Router>
</ul>
<div onClick={handleNavLinksToggle} className="hamburger-toggle">
<i className="fas fa-bars fa-lg"></i>
</div>
</nav>
)
}
export default Navbar;
App.js
import React from 'react';
import '../../src/App.css';
import Navbar from './Navbar';
import Wrapper from './Wrapper';
import {Content, Winnie, Testimonials, Values, Secrets, Footer} from '../components/Content';
function App() {
return (
<div>
<Navbar />
<Wrapper />
<Content />
<Winnie />
<Testimonials />
<Values />
<Secrets />
<Footer />
</div>
)
}
export default App;
These are few issues in your code:
App is your root React component, and you gave it a route: <Route path="/home" component={App}></Route>. This is causing a recursive / infinite loop. App component inside App component.
Code structure looks complex.
Here is a proposed fixed code:
index.jsx:
ReactDOM.render(<App />, document.getElementById("root"));
App.jsx:
export default function App() {
return (
<StrictMode>
<Routes />
</StrictMode>
);
}
Routes.jsx:
export default function Routes() {
return (
<Router>
{/* Route components would be visible only at their route */}
<Switch>
<Route exact path="/about" component={About}></Route>
<Route exact path="/services" component={Services}></Route>
<Route exact path="/contact" component={Contact}></Route>
</Switch>
{/* Below components would be visible always at UI */}
<Navbar /> {/* Top navigation Link's */}
<Wrapper />
<Content />
<Winnie />
<Testimonials />
<Values />
<Secrets />
<Footer /> {/* Bottom navigation Link's */}
</Router>
);
}
There are several things to keep in mind when using react-router.
The Router or BrowserRouter component should wrap all your routes and your links. Generally, if your app does not need more than one Router, its better to wrap your whole App with the Router.
The Link component's job is to simply navigate to the page and can be used anywhere you want to show a link to someplace e.g. in the Navbar.
The Route (not Router) component's placement is very important. It should be placed where you want to render the content. In your code you are rendering the routes in the Navbar and are unable to see the routes being rendered due to invalid / improper structure.
Navbar.js
Your Navbar should only contain the links while the Router should be on the top-level and the Switch / Routes should be placed where you want to render the content.
function Navbar() {
return (
<nav>
{/* Move `Router` to top-level e.g. in App.js */}
<ul>
<li className="link">
<Link to={"/home"}>Home</Link>
</li>
<li className="link">
<Link to={"/about"}>About</Link>
</li>
<li className="link">
<Link to={"/services"}>Services</Link>
</li>
<li className="link">
<Link to={"/contact"}>Contact</Link>
</li>
</ul>
{/* Move `Switch and Routes` to where you want to render the content e.g. in Content.js */}
</nav>
);
}
App.js
function App() {
return (
<Router>
<div>
<Navbar />
<Wrapper />
<Switch>
<Route path="/home" component={App}></Route>
<Route path="/about" component={About}></Route>
<Route path="/services" component={Services}></Route>
<Route path="/contact" component={Contact}></Route>
</Switch>
<Winnie />
<Testimonials />
<Values />
<Secrets />
<Footer />
</div>
</Router>
);
}

how to hide components on click of router link

I have my app.js in that App.js I want to show some components when clicked on that router.
Here is my App.js code
import React from 'react';
import {BrowserRouter, Route} from 'react-router-dom';
import Header from './Header'
import Slider from './Slider';
import Section from './Section';
import Footer from './Footer';
import Register from './Register';
class IndecisionApp extends React.Component {
render() {
return (
<BrowserRouter>
<div className="wrapper">
<Header/>
<Route path="/register" component={Register}></Route>
<Slider />
<Section />
<Footer />
</div>
</BrowserRouter>
);
}
}
export default IndecisionApp;\
I want to hide Section and Slider components when I click on Router.
You are doing react-router wrong.
The correct way is this
<BrowserRouter>
<div className="wrapper">
<Header/>
<Switch>
<Route path="/register" component={Register}></Route>
</Switch>
<Slider />
<Section />
<Footer />
</div>
</BrowserRouter>
more on react router
https://reacttraining.com/react-router/web/guides/quick-start
Let say you have a home component and that only show as landing page including section and slide.
So there should be basically two route one is for landing another is for register, like follows
<Route path="/register" component={Register} />
<Route exact path="/" component={Home} />
But as your home/landing/index page should show section and slide together so I change the home route a little bit and finally it sum up with the following code
<BrowserRouter>
<div className="wrapper">
<Header />
<div>
<Link to="/">Home</Link>
<Link to="/register">Register</Link>
</div>
<Switch>
<Route path={"/register"} component={Register} />
<Route
exact
path="/"
render={() => (
<React.Fragment>
<Home />
<Slider />
<Section />
</React.Fragment>
)}
/>
</Switch>
<Footer />
</div>
</BrowserRouter>
Please make me clear if your idea is something different

Categories