React Router 4: Internal Navigation Changes URL But Not View - javascript

I am pretty new to React and have gotten stuck on a React Router 4 issue that I can not solve, even after looking through all of the issues on this topic. I have a navigation section in the middle of my page that should switch the view on the bottom of the page depending on what I have clicked. When I click a new section in my navbar the url changes to the correct thing but my view does not update and instead is just left blank. Is there something wrong with my code causing this issue? See below:
My main navbar at the top of the page (which works) can be seen here:
import React, { Component } from "react";
import { Menu, Container, Button, Icon } from "semantic-ui-react";
import { NavLink, Link, withRouter } from 'react-router-dom'
import "semantic-ui-css/semantic.min.css";
class NavBar extends Component {
render() {
return (
<Menu inverted fixed="top">
<Container>
<Menu.Item as={Link} to="/" header>
<Icon name="angle double up" size="big" />
Atlas
</Menu.Item>
<Menu.Item as={NavLink} to="/profile" name="Profile" />
<Menu.Item as={NavLink} to="/budget" name="Budget" />
<Menu.Item position="right">
<Button as={Link} to="/settings" color="brown" animated="vertical">
<Button.Content hidden>Settings</Button.Content>
<Button.Content visible>
<Icon name="setting" />
</Button.Content>
</Button>
<Button as={Link} to="/proSignUp" color="brown" animated="fade">
<Button.Content visible>Sign-up for a Pro Account</Button.Content>
<Button.Content hidden>$5 a month</Button.Content>
</Button>
</Menu.Item>
</Container>
</Menu>
);
}
}
export default withRouter(NavBar);
My App.jsx file (which works) which contains the navbar and routes:
import React, { Component } from "react";
import { Container } from "semantic-ui-react";
import { Route, Switch } from "react-router-dom";
import ProfileDashboard from "../../features/profile/ProfileDashboard/ProfileDashboard";
import NavBar from "../../features/nav/NavBar/NavBar";
import HomePage from "../../features/home/HomePage";
import BudgetDashboard from "../../features/budget/BudgetDashboard/BudgetDashboard";
import SettingsPage from "../../features/settings/Settings"
import ProSignUp from "../../features/proSignUp/ProSignUp";
class App extends Component {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={HomePage} />
</Switch>
<Route
path="/(.+)"
render={() => (
<div>
<NavBar />
<Container className="main">
<Switch>
<Route path="/profile" component={ProfileDashboard} />
<Route path="/budget" component={BudgetDashboard} />
<Route path="/settings" component={SettingsPage} />
<Route path="/proSignUp" component={ProSignUp} />
</Switch>
</Container>
</div>
)}
/>
</div>
);
}
}
export default App;
My Profile Page navbar (which is changing routes) can be seen here:
import React from "react";
import { Grid, Menu } from "semantic-ui-react";
import { NavLink, withRouter } from 'react-router-dom'
const ProfileNav = () => {
return (
<Grid.Column width={16}>
<Menu horizontal="true" secondary>
<Menu.Item as={NavLink} to="/profile/overview">Overview</Menu.Item>
<Menu.Item as={NavLink} to="/profile/analytics">Analytics</Menu.Item>
</Menu>
</Grid.Column>
);
};
export default withRouter(ProfileNav);
And finally, my ProfileDashboard (which doesn't work) which contains my profile nav bar and routes can be seen below:
import React, { Component } from "react";
import { Grid, Divider } from "semantic-ui-react";
import { Switch, Route, Redirect } from "react-router-dom";
import ProfileHeader from "../ProfileHeader/ProfileHeader";
import ProfileNav from "../ProfileNav/ProfileNav";
import ProfileList from "../ProfileList/ProfileList";
import ProfileAnalytics from "../ProfileAnalytics/ProfileAnalytics";
const contentHash = {
headerContent: [
{
title: "Demographics",
age: 21,
sex: "male",
location: "Tucky, KY"
},
{
title: "Profession",
jobTitle: "Event Coordinator",
employer: "Tucky Tuck",
experience: "5 years",
preTaxIncome: 62000,
postTaxIncome: 44000
},
{
title: "Investments",
highRisk: "blah blah",
mediumRisk: "blah blah",
lowRisk: "blah blah"
},
{
title: "Retirement",
"401k": 415000,
RothIRA: 61000
}
]
};
class ProfileDashboard extends Component {
render() {
return (
<Grid>
<Grid.Column width={16}>
<ProfileHeader headerContent={contentHash.headerContent} />
<Divider />
<ProfileNav />
<Switch>
<Redirect exact from="/profile" to="/profile/overview" />
<Route path="profile/overview" component={ProfileList} />
<Route path="profile/analytics" component={ProfileAnalytics} />
</Switch>
</Grid.Column>
</Grid>
);
}
}
export default ProfileDashboard;

I figured it out after a lot of trial and error. I had to put both the components I was switching between inside a new component for the routing to work. So I made a new ProfileFooter component that held all of the routing logic and switched between the ProfileList and ProfileAnalytics page.
import React, { Component } from "react";
import { Switch, Route, Redirect } from "react-router-dom";
import ProfileList from "../ProfileList/ProfileList";
import ProfileAnalytics from "../ProfileAnalytics/ProfileAnalytics";
class ProfileFooter extends Component {
render() {
return (
<div>
<Switch>
<Redirect exact from="/profile" to="/profile/overview" />
<Route path="/profile/overview" component={ProfileList} />
<Route path="/profile/analytics" component={ProfileAnalytics} />
</Switch>
</div>
);
}
}
export default ProfileFooter;

Related

How to prevent page reoading after switching routes

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;

react router giving sub path for clicking again on link --v6

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 />
</>
);
}

unable to create triple nested routes in react router with material ui tabs

I'm trying to create triple nesting with react-router but it's not working.
Here is the expected output
I want to perform but I am trying to implement this with the router so my tab should get switch according to URL passed to it.
When the user just write / in the URL it should redirect to login page (working)
When the user write /dashboard it should redirect to the dashboard with side list (working)
When the user click on list items such as about in drawer of the dashboard then it should render about page.
Now my about page contains two tabs one for general and one for account (Everything was working until I implement this feature now the whole app has been crashed and not even showing the error)
Here is my code in sandbox. Note: I have commented on my third nested route because it was crashing entire app
App.js
import { Switch, Route } from "react-router-dom";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/" exact component={Login} />
</Switch>
</div>
);
}
Dashboard.js for nested routing
import { Switch, Redirect, Route, useRouteMatch } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import AppDrawerBar from "../components/AppDrawerBar";
const Dashboard = () => {
const { path } = useRouteMatch();
return (
<>
<AppDrawerBar>
<Switch>
<Route path={`${path}/about`} component={About} />
<Route path={`${path}/home`} component={Home} />
<Redirect to={`${path}/home`} />
</Switch>
</AppDrawerBar>
</>
);
};
export default Dashboard;
About.js third nested routing to handle tab navigation after clicking on about list item
import { Switch } from "#material-ui/core";
import React from "react";
import { Redirect, Route } from "react-router-dom";
import TabMenu from "../components/TabMenu";
const About = (props) => {
return (
<>
<h1>Welcome to about</h1>
{/* This doesn't work.. this code is to render tab navigation */}
{/* <Switch>
<Redirect exact from={`${props.path}/about`} to="about/general" />
<Route
exact
path="/about/:page?"
render={(props) => <TabMenu {...props} />}
/>
</Switch> */}
</>
);
};
export default About;
TabMenu.js to handle my tabs
import { Paper, Tab, Tabs } from "#material-ui/core";
import React, { useState } from "react";
import General from "../pages/General";
import Account from "../pages/Account";
const TabMenu = (props) => {
const { match, history } = props;
const { params } = match;
const { page } = params;
const tabNameToIndex = {
0: "about",
1: "contact"
};
const indexToTabName = {
about: 0,
contact: 1
};
const [selectedTab, setSelectedTab] = useState(indexToTabName[page]);
const handleChange = (event, newValue) => {
history.push(`/about/${tabNameToIndex[newValue]}`);
setSelectedTab(newValue);
};
return (
<>
<Paper elevation={2}>
<Tabs
value={selectedTab}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
>
<Tab label="General" />
<Tab label="Profile" />
</Tabs>
</Paper>
{selectedTab === 0 && <General />}
{selectedTab === 1 && <Account />}
</>
);
};
export default TabMenu;

React Where to place login page for a one page app with a side menu

I have a react web app with a sidemenu. Whenever a user clicks on the link in the sidemenu, they are routed to a page that is rendered at the right side of the sidemenu. My question is, how do I do login for such a usecase seeing as any page I route to renders to the right of the sidemenu. I want the login page to be full screen without the side menu showing. This is what App.js looks like.
import React, { Component } from "react";
import { HashRouter } from "react-router-dom";
import Navigation from "./pages/General/components/Navigation";
import SideMenu from "./pages/General/components/SideMenu";
import "../src/css/App.css";
class App extends Component {
render() {
return (
<div>
<HashRouter>
<div className="main-wrapper">
<SideMenu />
<Navigation />
</div>
</HashRouter>
</div>
);
}
}
export default App;
Here is Navigation.js
import React from "react";
import { Route } from "react-router-dom";
import CalendarPage from "../../Calendar/CalendarPage";
import DoctorsList from "../../Doctors/DoctorsList";
import PatientsList from "../../Patients/PatientsList";
import AdminUsersList from "../../AdminUsers/AdminUsersList";
import SpecialitiesList from "../../Specialities/SpecialitiesList";
const Navigation = () => {
return (
<div className="mainarea">
<Route exact path="/" component={CalendarPage} />
<Route exact path="/scheduler" component={CalendarPage} />
<Route exact path="/doctors" component={DoctorsList} />
<Route exact path="/patients" component={PatientsList} />
<Route exact path="/admin-users" component={AdminUsersList} />
<Route exact path="/specialities" component={SpecialitiesList} />
</div>
);
};
export default Navigation;
The best solution I can figure out in terms of a clean design, is to implement another router in your App.jsx, because you are implementing the routing inside your component, and you need another one for your login page.
Then, your App.jsx could be like this:
import React, { Component } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import LogIn from "./pages/General/components/Login";
import HomePage from "./pages/General/components/HomePage";
import "../src/css/App.css";
class App extends Component {
render() {
return (
<div>
<Switch>
<Route path={'/login'} component={LogIn} />
<Route path={'/'} component={HomePage} />
<Redirect to="/" />
</Switch>
</div>
);
}
}
export default App;
Then, for your HomePage do the following
import React, { Component } from "react";
import { HashRouter } from "react-router-dom";
import Navigation from "./pages/General/components/Navigation";
import SideMenu from "./pages/General/components/SideMenu";
import "../src/css/App.css";
class HomePage extends Component {
render() {
return (
<div>
<HashRouter>
<div className="main-wrapper">
<SideMenu />
<Navigation />
</div>
</HashRouter>
</div>
);
}
}
export default HomePage;
I hope it helps!
Here is my solution, it not exactly a solution, but it will give you a basic idea on how to implement this.
The idea is to place the Login component in app.js, and conditionally display it if the user is logged in.
You will have to pass a handler function to login component through which you will be able to control app.js state.
When login will be sucessfull, u can show the Navigation and Sidemenu component.
import { Fragment } from "react";
import Login from "path/to/login";
class App extends Component {
state = { isLoggedIn: false };
loginHandler = () => {
this.setState({
isLoggedIn: true
});
};
render() {
return (
<div>
<div className="main-wrapper">
{isLoggedIn ? (
<Fragment>
<SideMenu />
<Navigation />
</Fragment>
) : (
<Login loginHandler={this.loginHandler} />
)}
</div>
</div>
);
}
}
Also you need write a separate router file, which will contain the main app.
This is used to show the app component when navigated to /
import React from 'react';
import { HashRouter, Route } from 'react-router-dom';
import App from './app';
const MainRoute = () => (
<HashRouter>
<Route path="/" component={App} />
</HashRouter>
);
export default MainRoute;

How come react-router-dom does not change URL on click?

I am having trouble changing the URL and getting the component to load when using react-router-dom. If I manually enter the URL the component will load, however, the URL (and component) does not change when I click on the link. In this case, I am trying to load the '/industry/aerospace' page. Thanks!
Here's my code:
App.js:
import React from 'react'
import { compose } from 'redux'
import { Route, Switch, withRouter } from 'react-router-dom'
import { MuiThemeProvider } from '#material-ui/core/styles'
import LandingPage from '../../home'
import AnalyzerProductPage from '../../home/analyzer'
import MonitorProductPage from '../../home/monitor'
import Signout from '../../home/signout'
import Industry from '../../home/industry'
class App extends React.PureComponent {
render() {
return (
<div>
<MuiThemeProvider theme={muiTheme} >
<Switch>
<Route exact path="/" component={LandingPage} />
<Route exact path="/version" component={Version} />
<Route exact path="/signout_success" component={LandingPage} />
<Route exact path="/signout" component={Signout} />
<Route exact path="/liquidtool-analyzer" component={AnalyzerProductPage} />
<Route exact path="/liquidtool-monitor" component={MonitorProductPage} />
<Route exact path="/industry/:industry" component={Industry} />
</Switch>
</MuiThemeProvider>
<NotificationHandler />
<RestCallProgressBar />
</div>
)
}
}
export default compose(
withRouter,
)(App)
HomeHeader.js (with link to page):
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
// material-ui
import { withStyles } from '#material-ui/core/styles'
class HomeHeader extends Component {
state = {
value: false,
anchorEl: null
};
handleIndustriesClick = (event) => {
this.setState({ anchorEl: event.currentTarget })
};
handleIndustriesClose = (pageID) => {
this.setState({ anchorEl: null })
}
handleDashboardClick = () => {
this.props.history.push('/dashboard')
};
handleChange = (event, value) => {
this.setState({ value })
};
render() {
const { classes } = this.props
const { value } = this.state
const { anchorEl } = this.state
return (
<AppBar className={classes.appBar} elevation={this.props.elevation}>
<Hidden smDown>
<Grid container justify="space-between" alignItems="center">
<Tabs value={value} onChange={this.handleChange}>
<Tab label="monitor" />
<Tab label="sensor" />
<Tab
label="industries"
aria-owns={anchorEl ? 'industries-menu' : null}
aria-haspopup={true}
onClick={this.handleIndustriesClick}
/>
<Menu
id="industries-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={this.handleIndustriesClose}
>
<MenuItem onClick={this.handleIndustriesClose}><Link to={'/industry/aerospace'}></Link>Aerospace</MenuItem>
</Menu>
</Tabs>
</Grid>
</Hidden>
</AppBar>
)
}
}
export default withStyles(styles)(HomeHeader)
Industry.js (Component to load):
import React, { Component } from 'react';
import Typography from '#material-ui/core/Typography';
import HomeHeader from '../components/HomeHeader'
class Industry extends Component {
render() {
return(
<div>
<HomeHeader />
<Typography>Hey</Typography>
</div>
)
}
}
export default Industry
try to wrap MenuItem from Link component:
<Link to='/industry/aerospace'>
<MenuItem>
Aerospace
</MenuItem>
</Link>
since you are using material-ui you can also use component prop in MenuItem (which will set the component as root)
<MenuItem component={Link} to='/industry/aerospace'>
Aerospace
</MenuItem>
hope this will help you

Categories

Resources