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

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

Related

In how many ways we can pass props to all child components in react

I have an app code
import React from "react";
import { Route, Switch } from "react-router-dom";
import Minidrawer from './components/Drawer/Minidrawer'
import { makeStyles } from '#mui/styles';
import Box from '#mui/material/Box';
import Main from "./components/Main/Main";
import {useSelector} from 'react-redux'
const useStyles = makeStyles({
container: {
display: "flex"
}
});
export default function App() {
const classes = useStyles();
const user = useSelector((state) => state.auth);
return (
<Box sx={{ display: 'flex' }}>
<Minidrawer currUser={user}/>
<Switch>
<Route exact from="/" render={props => <Main childText="home" currUser={user} {...props} />} />
<Route exact path="/auth" render={props => <Main childText="auth" currUser={user} {...props} />} />
<Route exact path="/register-client" render={props => <Main childText="registerClient" currUser={user} {...props} />} />
</Switch>
</Box>
);
}
I have to pass currUser to all child components imported in App but I do not want to duplicate the code, what are different ways to achieve this so that all of the components have access to currUser?
if I understand what you want to do, you want to pass props to all children of a component, if the components are simple components you can do as follows:
import React from "react";
import Main from "./Main";
import PassPropsToNormalComponents from "./PassPropsToNormalComponents";
export default function App() {
const user = {
username: "lakhdar"
};
return (
<div style={{ display: "flex" }}>
<PassPropsToNormalComponents currUser={user}>
<Main childText="home" />
<Main childText="auth" />
<Main childText="registerClient" />
</PassPropsToNormalComponents>
</div>
);
and this is the PassPropsToNormalComponents file
import React from "react";
export default function PassPropsToNormalComponents({ children, ...props }) {
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { ...child.props, ...props });
}
return child;
});
return <>{childrenWithProps}</>;
}
but in your case passing the props to the routes wont' make the routes pass the props to their rendered components so we need an extra step here:
first the file where we provide the props to the parent:
import React from "react";
import { Route, Switch } from "react-router-dom";
import Main from "./Main";
import PassPropsToRouteComponents from "./PassPropsToRouteComponents";
export default function App() {
const user = {
username: "lakhdar"
};
return (
<div style={{ display: "flex" }}>
<Switch>
<PassPropsToRouteComponents currUser={user}>
<Route
exact
from="/"
render={(props) => {
return <Main childText="home" {...props} />;
}}
/>
<Route
exact
path="/auth"
render={(props) => <Main childText="auth" {...props} />}
/>
<Route
exact
path="/register-client"
render={(props) => <Main childText="registerClient" {...props} />}
/>
</PassPropsToRouteComponents>
</Switch>
</div>
);
}
and finally, the extra step is to get the rendered element and pass it its own props + the props from the parent, and the file looks like this:
import React from "react";
export default function PassPropsToRouteComponents({ children, ...props }) {
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
const routerChild = child.props.render();
return React.cloneElement(child, {
...child.props,
render: () => {
return React.cloneElement(routerChild, {
...routerChild.props,
...props
});
}
});
}
return child;
});
return <>{childrenWithProps}</>;
}
link to working codesandbox: https://codesandbox.io/s/gracious-meadow-dj53s
I hope this is what you've been looking for.
You could use redux or the context API.
Redux: https://react-redux.js.org/
Context API: https://reactjs.org/docs/context.html

scrolling has some problems

i using coreUi template for my reactJS project & in that when some component scrolling i can't have smooth scrolling which means i can scroll about bit first time and then again i have move courser bit and do the scrolling to go down of page..this is only happens with small screens (ex 1280px * 1024)
this is a gif file which showing the problem :
and here is the place where my all components are handling :
import React, { Component, Suspense, Fragment } from "react";
import { Route, BrowserRouter as Router } from "react-router-dom";
import * as router from "react-router-dom";
import { Container } from "reactstrap";
import { logout } from "../../actions/authActions";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import {
AppHeader,
AppSidebar,
AppSidebarFooter,
AppSidebarForm,
AppSidebarHeader,
AppSidebarMinimizer,
AppBreadcrumb2 as AppBreadcrumb,
AppSidebarNav2 as AppSidebarNav
} from "#coreui/react";
// sidebar nav config
import NavigationBar from "../../menu";
// routes config
import routes from "../../routes";
import { connect } from "react-redux";
import { loading } from "./LoadingComponent";
import ButtonPermission from "../../permission";
const DefaultHeader = React.lazy(() => import("./DefaultHeader"));
const Dashboard = React.lazy(() => import("./../../views/Dashboard/Dashboard"));
class DefaultLayout extends Component {
constructor() {
super();
this.currentNavigation = new NavigationBar().createMenu();
this.routes = new ButtonPermission().setPermission(routes);
}
signOut(e) {
e.preventDefault();
this.props.history.push("/login");
this.props.LOGOUT();
}
render() {
const divProps = Object.assign({}, this.props);
delete divProps.LOGOUT;
return (
<div className="app scroller">
<AppHeader fixed>
<Suspense fallback={loading()}>
<DefaultHeader onLogout={e => this.signOut(e)} />
</Suspense>
</AppHeader>
<div className="app-body">
<AppSidebar fixed display="lg">
<AppSidebarHeader />
<AppSidebarForm />
<Suspense>
<AppSidebarNav
navConfig={this.currentNavigation}
{...divProps}
router={router}
/>
</Suspense>
<AppSidebarFooter />
<AppSidebarMinimizer />
</AppSidebar>
<main className="main">
<AppBreadcrumb appRoutes={this.routes} router={router} />
<Container fluid>
<Suspense fallback={loading()}>
{this.routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
name={route.name}
render={props => (
<route.component {...props} {...route.props} />
)}
/>
) : (
<Fragment>
<Router
path="*"
name="home"
render={props => <Dashboard {...props} />}
/>
</Fragment>
////
////
);
})}
</Suspense>
<ToastContainer
autoClose={3000}
hideProgressBar
closeOnClick
pauseOnHover={false}
position="bottom-center"
/>
</Container>
</main>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
error: state.error
});
const mapDispachToProps = dispach => {
return {
LOGOUT: () => dispach(logout())
};
};
export default connect(mapStateToProps, mapDispachToProps)(DefaultLayout);
can i get more smooth scrolling in my reactJS project please! Thank you!

React refresh component on login

I have 2 components, NavBar which contains a login modal and the 'body' of page.
I want to detect when a user logs in and re-render the page based on that. How do I update the login prop in the second component when I log in using the modal of the first one?
A simplified version of the code to keep it short:
// NavBar.js
export default class NavBar extends Component {
constructor(props) {
super(props)
this.initialState = {
username: "",
password: "",
loginModal: false
}
this.handleLogin = this.handleLogin.bind(this)
}
handleLogin(e) {
e.preventDefault()
loginAPI.then(result)
}
render() {
return( <nav> nav bar with links and login button </nav>)
}
// Some random page
export default class Checkout extends Component {
constructor(props) {
super(props);
this.state = {
order_type: 'none',
loggedIn: false
}
this.Auth = new AuthService()
}
componentDidMount() {
if (this.Auth.loggedIn()) {
const { username, email } = this.Auth.getProfile()
this.setState({ loggedIn: true, email: email })
}
try {
const { order_type } = this.props.location.state[0]
if (order_type) {
this.setState({ order_type: order_type })
}
} catch (error) {
console.log('No package selected')
}
}
componentDidUpdate(prevProps, prevState) {
console.log("this.props, prevState)
if (this.props.loggedIn !== prevProps.loggedIn) {
console.log('foo bar')
}
}
render() {
return (
<section id='checkout'>
User is {this.state.loggedIn ? 'Looged in' : 'logged out'}
</section>
)
}
}
// App.js
function App() {
return (
<div>
<NavBar />
<Routes /> // This contains routes.js
<Footer />
</div>
);
}
// routes.js
const Routes = () => (
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/register" component={Register} />
<Route exact path="/registersuccess" component={RegisterSuccess} />
<Route exact path="/faq" component={FAQ} />
<Route exact path="/checkout" component={Checkout} />
<Route exact path="/contact" component={Contact} />
{/* <PrivateRoute exact path="/dashboard" component={Dashboard} /> */}
<Route path="/(notfound|[\s\S]*)/" component={NotFound} />
</Switch>
)
I would recommend using the react context API to store information about the logged in user.
See: https://reactjs.org/docs/context.html
Example
auth-context.js
import React from 'react'
const AuthContext = React.createContext(null);
export default AuthContext
index.js
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import AuthContext from './auth-context.js'
const AppWrapper = () => {
const [loggedIn, setLoggedIn] = useState(false)
return (
<AuthContext.Provider value={{ loggedIn, setLoggedIn }}>
<App />
</AuthContext.Provider>
)
}
ReactDOM.render(
<AppWrapper/>,
document.querySelector('#app')
)
Then inside any component you can import the AuthContext and use the Consumer component to check if the user is logged in order set the logged in state.
NavBar.js
import React from 'react'
import AuthContext from './auth-context.js'
const NavBar = () => (
<AuthContext.Consumer>
{({ loggedIn, setLoggedIn }) => (
<>
<h1>{loggedIn ? 'Welcome' : 'Log in'}</h1>
{!loggedIn && (
<button onClick={() => setLoggedIn(true)}>Login</button>
)}
</>
)}
</AuthContext.Consumer>
)
export default NavBar
HOC version
with-auth-props.js
import React from 'react'
import AuthContext from './auth-context'
const withAuthProps = (Component) => {
return (props) => (
<AuthContext.Consumer>
{({ loggedIn, setLoggedIn }) => (
<Component
loggedIn={loggedIn}
setLoggedIn={setLoggedIn}
{...props}
/>
)}
</AuthContext.Consumer>
)
}
export default withAuthProps
TestComponent.js
import React from 'react'
import withAuthProps from './with-auth-props'
const TestComponent = ({ loggedIn, setLoggedIn }) => (
<div>
<h1>{loggedIn ? 'Welcome' : 'Log in'}</h1>
{!loggedIn && (
<button onClick={() => setLoggedIn(true)}>Login</button>
)}
</div>
)
export default withAuthProps(TestComponent)
Alternatively if you have redux setup with react-redux then it will use the context API behind the scenes. So you can use the connect HOC to wrap map the logged in state to any component props.

React Router 4: Internal Navigation Changes URL But Not View

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;

The correct login page pattern

I am sorry for my stupid question but i am really new in react and this problem make me stuck for days. I am kinda confused to make a login page in reactjs. my app.js code is like this :
import React from 'react';
import {HashRouter as Router, Route} from 'react-router-dom';
import asyncComponent from './AsyncComponent';
import AppShell from './AppShell';
import Login from './login/Login';
const Dashboard = asyncComponent(() => {
return import(/* webpackChunkName: "dashboard" */ './dashboard/Dashboard')
.then(module => module.default);
});
const LoginPage = asyncComponent(() => {
return import(/* webpackChunkName: "login" */ './login/Login')
.then(module => module.default);
});
class App extends React.Component {
render() {
return (
<Router>
<AppShell>
<div>
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginPage} />
</div>
</AppShell>
</Router>
);
}
}
export default App;
And this is my AppShell code :
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
import {MuiThemeProvider} from 'material-ui/styles';
import {AppBar, Drawer, MenuItem} from 'material-ui';
import {DashboardIcon} from './icon/Icons';
import ArrowDropRight from 'material-ui/svg-icons/navigation-arrow-drop-right';
const ContentStyle = {
width: '90%',
margin: 'auto',
marginTop: '30px'
};
class SidebarDrawer extends React.Component {
componentDidMount() {
let frameCount = 0;
const open = () => (frameCount++ > 0) ? this.props.onMounted() :
requestAnimationFrame(open);
requestAnimationFrame(open);
}
render() {
return (
<Drawer
docked={false}
width={200}
open={this.props.open}
onRequestChange={this.props.onRequestChange}
>
<MenuItem
primaryText={'Dashboard'}
leftIcon={<DashboardIcon/>}
containerElement={<Link to={'/'}/>}
onClick={this.props.onClick}
/>
</Drawer>
);
}
}
class AppShell extends Component {
constructor(props) {
super(props);
this.state = {
open: false,
drawer : false
};
}
handleDrawerToggle = (e) => {
if (!this.state.drawer) {
this.setState({drawer: true});
e.preventDefault();
} else {
this.setState({open: !this.state.open});
}
}
render() {
const LazySidebarDrawer = this.state.drawer && (<SidebarDrawer
open={this.state.open}
onMounted={() => this.setState({open: true})}
onClick={() => this.setState({open: false})}
onRequestChange={open => this.setState({open: open})}
/>)
return (
<MuiThemeProvider>
<div>
<AppBar
title="Dashboard"
iconClassNameRight="muidocs-icon-navigation-expand-more"
onLeftIconButtonTouchTap={this.handleDrawerToggle}
/>
{LazySidebarDrawer}
<div id="content" style={ContentStyle}>
{React.cloneElement(this.props.children)}
</div>
</div>
</MuiThemeProvider>
);
}
};
export default AppShell;
But i still can access dashboard when i open login page. How is the correct pattern for login page?
Thanks
Your routing is correct, the exact '/' will only render the Dashboard component when the path is '/'. What you're seeing is the dashboard drawer or AppBar component. The dashboard drawer is still there in the login screen because it's always there in the AppShell code and your routes are children of AppShell. A potential solution would be to move that AppBar component to your Dashboard component if you only want it there.

Categories

Resources