My sidebar uses react-router-dom to switch between page files using routes, however when I switch routes, my page reloads. Is there a way I can switch routes without reloading the page?
SideBar
import React from 'react'
import {SideNav_Data} from "./SideNav_Data"
import Sunrise from './Sunrise.png'
function SideNav() {
return (
<div className = "SideNav">
<img src={Sunrise} className='SideNavImg' alt=''/>
<ul className="SideNavList">
{SideNav_Data.map((val, key) => {
return (
<li key={key}
className = "row"
id={window.location.pathname === val.link ? "active" : ""}
onClick = {() => {
window.location.pathname = val.link;
}}>
<div id='icon'>{val.icon}</div>{" "}<div id='title'>{val.title}</div>
</li>
)
})}
</ul>
</div>
)
}
export default SideNav
Sidebar Data
import React from 'react'
import CottageIcon from '#mui/icons-material/Cottage';
import DirectionsCarFilledIcon from '#mui/icons-material/DirectionsCarFilled';
import PersonIcon from '#mui/icons-material/Person';
import AccountBalanceIcon from '#mui/icons-material/AccountBalance';
import AccountCircleIcon from '#mui/icons-material/AccountCircle';
import ContactSupportIcon from '#mui/icons-material/ContactSupport';
export const SideNav_Data = [
{
title: "Home",
icon: <CottageIcon />,
link: "/"
},
{
title: "Cars",
icon: <DirectionsCarFilledIcon />,
link: "/Cars"
},
{
title: "Representatives",
icon: <PersonIcon />,
link: "/Representatives"
},
{
title: "Loan Estimator",
icon: <AccountBalanceIcon />,
link: "/Loan-Estimator"
},
{
title: "Account",
icon: <AccountCircleIcon />,
link: "/Account"
},
{
title: "Support",
icon: <ContactSupportIcon />,
link: "/Support"
},
]
Home page
import React from 'react'
import SideNav from '../Components/SideNav'
function Home() {
return (<div>
<SideNav />
</div>
)
}
export default Home
App.js
import './App.css';
import { BrowserRouter as Router, Routes, Route} from "react-router-dom"
import Home from './Pages/Home';
import Cars from './Pages/Cars';
import Reps from './Pages/Reps';
import LoanEst from './Pages/LoanEst';
import Account from './Pages/Account';
import Support from './Pages/Support';
function App() {
return (
<Router>
<Routes>
<Route path='/' element={<Home />}/>
<Route path="/Cars" element={<Cars />}/>
<Route path="/Representatives" element={<Reps />}/>
<Route path="/Loan-Estimator" element={<LoanEst />}/>
<Route path="/Account" element={<Account />}/>
<Route path="/Support" element={<Support />}/>
</Routes>
</Router>
)
}
export default App;
I've tried switching the "link: " part of the sidebar data to <Link to={Home /}/> but that resulted in the sidebar completely disappearing.
You should use the Link component instead of changing window.location.pathname:
{SideNav_Data.map((val, key) => {
return (
<Link
key={key}
className = "row"
id={window.location.pathname === val.link ? "active" : ""}
to={val.link}
>
<div id='icon'>{val.icon}</div>{" "}<div id='title'>{val.title}</div>
</Link>
)
})}
Issues
Using window.location.pathname to set the current URL path will necessarily trigger a page reload and remount the entire app. Use one of the link components exported from react-router-dom to issue declarative navigation actions within the app.
The SideNav component disappears because it is only rendered when the Home component is rendered when the path is "/". Use a layout route to render the SideNav component
Solution
I suggest importing and using the NavLink component as it's a special version of the Link component that applies an "active" classname by default. If you need to compute the id attribute for the li element then use the useLocation hook to access the current location.pathname value.
Example:
import { NavLink, useLocation } from 'react-router-dom';
...
function SideNav() {
const { pathname } = useLocation();
return (
<div className="SideNav">
<img src={Sunrise} className='SideNavImg' alt='' />
<ul className="SideNavList">
{SideNav_Data.map((val) => (
<li
key={val.link}
className="row"
id={pathname === val.link ? "active" : ""}
>
<NavLink to={val.link} end>
<div id='icon'>{val.icon}</div>
{" "}
<div id='title'>{val.title}</div>
</NavLink>
</li>
))}
</ul>
</div>
);
}
Render the SideNav as part of a layout route so it's conditionally rendered with the routes you want it to be rendered with.
Example:
import {
BrowserRouter as Router,
Routes,
Route,
Outlet,
} from "react-router-dom"
import Home from './Pages/Home';
import Cars from './Pages/Cars';
import Reps from './Pages/Reps';
import LoanEst from './Pages/LoanEst';
import Account from './Pages/Account';
import Support from './Pages/Support';
import SideNav from './Components/SideNav';
const SideNavLayout = () => (
<>
<SideNav />
<Outlet />
</>
);
function App() {
return (
<Router>
<Routes>
<Route element={<SidNavLayout />}>
<Route path='/' element={<Home />} />
<Route path="/Cars" element={<Cars />} />
<Route path="/Representatives" element={<Reps />} />
<Route path="/Loan-Estimator" element={<LoanEst />} />
<Route path="/Account" element={<Account />} />
<Route path="/Support" element={<Support />} />
</Route>
</Routes>
</Router>
);
}
Home
import React from 'react';
function Home() {
return (
<div>
...
</div>
);
}
export default Home;
Related
import React from "react";
// import { BrowserRouter as Router, Route, Switch} from "react-router-dom"
import Home from "./Home";
import Header from "./Header";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const App = () => {
return (
//BEM
<Router>
<div className="app">
{/* Switch case */}
<Switch>
{/* checkout page */}
<Route path="/checkout" >
<Header/>
{/* Header */}
<h1>I am a Checkout, Smash the like button</h1>
</Route>
<Route path="/" >
<Header/>
<Home/>
</Route>
</Switch>
</div>
</Router>
);
};
export default App;
I am update the code with the help of Routes, but my page is gone. So what do I convert?
import React from "react";
import Home from "./Home";
import Header from "./Header";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
const App = () => {
return (
//BEM
<Router>
<div className="app">
<Routes>
{/* checkout page */}
<Route path="/checkout" element={<Header/>} >
<h1>I am a Checkout, Smash the like button</h1>
<Route path="/" element={Header/>} />
//Here I want both page link in one path Header and Home.
</Route>
</Routes>
</div>
</Router>
);
};
export default App;
From what I can understand of your question you are trying to render a common Header component with each route. For this you can render the Header component on a layout route that renders also an Outlet component for nested children routes.
Example:
import React from "react";
import Home from "./Home";
import Header from "./Header";
import { BrowserRouter as Router, Routes, Route, Outlet } from "react-router-dom";
const Layout = () => (
<>
<Header />
<Outlet />
</>
);
const App = () => {
return (
<Router>
<div className="app">
<Routes>
<Route element={<Layout />}>
{/* checkout page */}
<Route
path="/checkout"
element={<h1>I am a Checkout, Smash the like button</h1>}
/>
<Route path="/" element={<Home />} />
</Route>
</Routs>
</div>
</Router>
);
};
See nested routes in the documentation for more details.
Im trying to create a PrivateRoutes in addition to the regular routes.
After login, the page is successfully re-direct to /home, however when I tried to open /work, the page will go back to /home. all the data from state.valid is also shows "unidentified" in /work.
I figured it out that inside the privateRoutes it check if valid.isAuthenticated is true or not. However since. valid.isAuthenticated is set to false as initial value in reducer, everytime I open /home or /work, it re-render /login and then render /home or /work.
How do I fix to not to render /login before opening other pages?
Here is my PrivateRoutes.js
import React from "react";
import { useSelector } from "react-redux";
import { Navigate } from "react-router-dom";
const PrivateRoute = ({ children }) => {
const valid = useSelector((state) => state.valid);
return valid.isAuthenticated ? children : <Navigate to="/login" />;
};
export default PrivateRoute;
here is my AppRouter.js
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import App from "../components/App";
import Login from "../components/Login";
import HomePage from "../components/HomePage";
import WorkPage from "../components/WorkPage";
const AppRouter = () => (
<BrowserRouter>
<div>
<NavigationBar />
<div>
<Routes>
<Route path="/" element={<App />} exact />
<Route path="/login" element={<Login />} exact />
<Route path="/home" element={<PrivateRoute><HomePage /></PrivateRoute>} />
<Route path="/work" element={<PrivateRoute><WorkPage /></PrivateRoute>} />
</Routes>
</div>
</div>
</BrowserRouter>
);
export default AppRouter;
useEffect(() => {
if (valid.isAuthenticated) {
navigate("/home");
}
},[valid.isAuthenticated]);
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import App from "../components/App";
import Login from "../components/Login";
import HomePage from "../components/HomePage";
import WorkPage from "../components/WorkPage";
const AppRouter = () => {
useEffect(() => {
if (!valid.isAuthenticated) {
navigate("/login");
}
});
return (
<BrowserRouter>
<div>
<NavigationBar />
<div>
{valid.isAuthenticated ? <>
<Routes>
<Route path="/" element={<App />} exact />
<Route path="/home" element={<HomePage />} />
<Route path="/work" element={<WorkPage />} />
</Routes>
</>
:
<>
<Routes>
<Route path="/login" element={<Login />} exact />
</Routes>
</>
}
</div>
</div>
</BrowserRouter>
)
};
export default AppRouter;
here I am building a website and I am using react-router-dom
everything seems to work fine when I use the Navbar and Footer components in every page.
So I come up with idea to wrap the component in a wrapper which contains Navbar and Footer.
When I click on Link or maybe NavLink, it seems to work fine but when I click again on same or another link in navbar then it navigates to a sub path under the previous selected path.
like this
on Single Click:
http://localhost:3000/projects
on clicking the same link again:
http://localhost:3000/projects/projects
App.js
import './App.css';
import {BrowserRouter as Router, Route, Outlet, Routes} from 'react-router-dom'
import {CommonScreen, Wrapper, About, Email, Projects} from './Barell'
function App() {
return (
<>
<Router>
<Routes>
<Route index element={<Wrapper children={<CommonScreen/>}/>}/>
<Route path='about' element={<Wrapper children={<About/>}/>}/>
<Route path='projects' element={<Wrapper children={<Projects/>}/>}/>
<Route path='email' element={<Email/>}/>
</Routes>
</Router>
<Outlet/>
</>
);
}
export default App;
Navbar.jsx:
import React from 'react'
import '../../index.css'
import { NavLink } from 'react-router-dom'
const links = [
{
name:"Home",
slug:"/",
},
{
name:"Projects",
slug:"projects",
},
{
name:"About",
slug:"about",
},
{
name:"Contact",
slug:"email",
},
]
export const Navbar = () => {
let activeStyle = {
textDecoration: "underline",
};
return (
<>
<nav>
<div id='brand'>Prema<span>culture</span></div>
<ul id='links'>
{
links.map((current,index) => (
<li>
<NavLink
key={index}
to={current.slug}
style={({ isActive }) =>
isActive ? activeStyle : undefined
}
>
{current.name}
</NavLink>
</li>
))
}
</ul>
</nav>
</>
)
}
Wrapper.jsx:
import React from 'react'
import { Navbar, Footer } from '../Barell'
export const Wrapper = ({children}) => {
return (
<>
<Navbar/>
{children}
<Footer/>
</>
)
}
You are using relative paths (versus absolute), so when already on a route, say "/projects", and then click a link that navigates to "about", the code links relative from "/projects" and results in "/projects/about".
To resolve you can make all the link paths absolute by prefixing a leading "/" character. Absolute paths begin with "/".
Example:
const links = [
{
name:"Home",
slug:"/",
},
{
name:"Projects",
slug:"/projects",
},
{
name:"About",
slug:"/about",
},
{
name:"Contact",
slug:"/email",
},
];
Additionally, to make the code more DRY you might also want to convert the Wrapper component into a layout route. Layout routes render an Outlet component for nested routes to be rendered into instead of a children prop for a single routes content.
Example
import React from 'react';
import { Outlet } from 'react-router-dom';
import { Navbar, Footer } from '../Barell';
export const Layout = () => {
return (
<>
<Navbar/>
<Outlet />
<Footer/>
</>
)
}
...
function App() {
return (
<>
<Router>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<CommonScreen />} />
<Route path='about' element={<About />} />
<Route path='projects' element={<Projects />} />
</Route>
<Route path='email' element={<Email />} />
</Routes>
</Router>
<Outlet />
</>
);
}
I have some problem with react-router. It doesn't go to Edit page when I click on id. The id is in URL, but it doesn't do anything.
const Main = (props) => {
const { pathname } = props.location;
return (
<Fragment>
<div>
<div className="container">
<Header />
{pathname === "/create" ? <Create /> : null}
{pathname === '/edit/:id' ? <Edit /> : null}
{pathname === "/" ? <Home /> : null}
</div>
</div>
</Fragment>
);
};
export default withRouter(Main);
app.js:
require('./components/Index');
import React from 'react'
import ReactDOM from "react-dom"
import Index from "./components/Index";
import { BrowserRouter as Router} from "react-router-dom";
import { ToastContainer } from 'react-toastify';
const App =() =>{
}
if (document.getElementById('app')) {
ReactDOM.render(<Router> <Index /> <ToastContainer /></Router>, document.getElementById('app'));
}
index.js:
import React from "react";
import { Route, Switch } from "react-router-dom";
import Main from "./CRUD/Main";
import Create from "./CRUD/Create";
import Edit from "./CRUD/Edit";
import Home from "./CRUD/Home";
const Index = (props) => {
return (
<Main>
<Switch>
<Route path="/Create" component={Create} />
<Route path='/edit/:id' component={Edit} />
<Route exact path="/" component={Home} />
</Switch>
</Main>
);
};
export default Index;
I think main.js have some problems with pathname.
You don't need to do conditional rendering like you are doing in Main when using react-router. Switch will automatically render the first child <Route> or <Redirect> that will match the location.
Hence, you need to remove Main from your router component i.e. Index so that it looks like as shown below:
const Index = (props) => {
return (
<>
<NavBar /> {/* NavBar is optional, I just added for example */}
<Switch>
<Route path="/create" component={Create} />
<Route path="/edit/:id" component={Edit} />
<Route exact path="/" component={Home} />
</Switch>
</>
);
}
NavBar: (Links; just for example)
function NavBar() {
return (
<ul>
<li>
<Link to="/create">Go to create</Link>
</li>
<li>
<Link to="/edit/123">Go to edit with id = 123</Link>
</li>
<li>
<Link to="/">Go to home</Link>
</li>
</ul>
);
}
Now, when you click on the above links, it will automatically take you to the related component (as declared in routes i.e. Index). (no manually condition checking)
And example, to retrieve the URL param i.e. id in Edit component using useParams hook:
function Edit() {
const { id } = useParams<{ id: string }>(); // Remove <{ id: string }> if not using TypeScript
return <h2>Edit, {id}</h2>;
}
I can get the data of the players when i Route to the Players Component,
but when i click on the Link Tags, the PLayersContainer Component is not opening.
This is my App.js File.
import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import Players from './Components/Players'
import PlayersContainer from './Components/Container/playersContainer'
import Navigation from './Components/Navigation';
export default class App extends Component {
state = {
players:[
{
id:1,
name:'Ronaldo'
},
{
id:2,
name:'Messi'
}
]
}
render() {
return (
<Router>
<Navigation />
<Switch>
<Route path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route exact path="/players/:id" render={PlayersContainer} />
</Switch>
</Router>
)
}
}
This is my Players Component.
import React from 'react'
import { Link } from 'react-router-dom'
export default function Players(props) {
const renderPlayers = () => {
let players = props.players.map(playerObj => <li> <Link to={`/players/${playerObj.id}`}> Player {playerObj.name} </Link></li>)
return players
}
return (
<div>
<ul>
{renderPlayers()}
</ul>
</div>
)
}
This is my PlayersContainer Component, where i want to render the individual data of the Player.
import React from 'react'
import { Link } from 'react-router-dom'
export default function PlayersContainer(props) {
const renderPlayers = () => {
console.log(props);
}
return (
<div>
<ul>
{renderPlayers()}
</ul>
</div>
)
}
You have the wrong Route marked as exact. The way its written currently, anything beginning with /players will match the first route. Since its in a switch, only the first match will be rendered.
Change it from:
<Route path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route exact path="/players/:id" render={PlayersContainer} />
to this:
<Route exact path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route path="/players/:id" render={PlayersContainer} />
Now only exactly /players will match the first route, and /players/id can continue past it to match the second.