import React, { Component } from "react";
import { Col, Image, Nav } from "react-bootstrap";
import { NavLink } from "react-router-dom";
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
console.log("sidebar");
}
render() {
return (
<Col md={3} lg={2} className="sidebar">
<div className="sidebarNav">
<NavLink to="/admin/dashboard" className="active">
<span className="icon-dashboard"></span> DASHBOARD
</NavLink>
<NavLink to="/admin/deliveryagentlist">
<span className="icon-delivery-agent"></span> DELIVERY AGENTS
</NavLink>
<NavLink to="/admin/customerlist">
<span className="icon-customers"></span> CUSTOMERS
</NavLink>
</div>
</Col>
);
}
}
export default Sidebar;
The above is the code of sidebar in my project.
The issue I am facing is when I change the page by clicking on sidebar items routing takes place. But it takes too long to redirect the page
I am on the dashboard. I click the customer in the sidebar. then the URL in the browser will change from http://localhost:3000/admin/dashboard to http://localhost:3000/admin/customerlist within no time. But the page loading takes too long. The customer list page contains some initial consoled text in its componentDidMount, like componentDidMount() { console.log("hey. Customer"); this.getList();}
and I observed the text hey. Customer also get consoled too late. The API calling inside this.getList() is also taking longer to start.
I am providing my routing code. It may be linked with routing.
MainRouting.js
import React, { Component } from "react";
import { Switch, Route } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
// Route
import PrivateRouteAdmin from "./PrivateRouteAdmin";
import PublicRoute from "./PublicRoute";
// admin
import Login from "../Component/admin/login/Login";
import AdminDashboard from "../Component/admin/dashboard/Dashboard";
import AdminCustomerTable from "../Component/admin/customer/CustomerTable";
import DeliveryAgentTable from "../Component/admin/DeliveryAgent/DeliveryAgentTable";
class MainRoute extends Component {
render() {
return (
<>
<Switch>
<PublicRoute exact path="/" component={Login} />
<PrivateRouteAdmin
path="/admin/dashboard"
component={AdminDashboard}
/>
<PrivateRouteAdmin
path="/admin/customerlist"
component={AdminCustomerTable}
/>
<PrivateRouteAdmin
path="/admin/deliveryagentlist"
component={DeliveryAgentTable}
/>
<PublicRoute path="/admin/login" component={Login} />
</Switch>
</>
);
}
}
export default MainRoute;
PrivateRouteAdmin
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { getLocalStorage } from "../common/helpers/Utils";
const PrivateRouteAdmin = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
getLocalStorage("adminInfo") ? (
<Component {...props} {...rest} />
) : (
<Redirect
to={{ pathname: "/admin/login", state: { from: props.location } }}
/>
)
}
/>
);
export default PrivateRouteAdmin;
PublicRoute
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { getLocalStorage } from "../common/helpers/Utils";
const PublicRoute = ({ component: Component, restricted, ...rest }) => {
return (
<Route
{...rest}
render={props =>
getLocalStorage("adminInfo") ? (
<Redirect to="/admin/dashboard" />
) : (
<Component {...props} />
)
}
/>
);
};
export default PublicRoute;
Related
I have an app that uses styled-components and material-ui. I have private routes configurated, based on the user role and if the user is not authenticated I should return him to login page. But when that happen the layout do no load.
App.tsx
import React from 'react';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core';
import { Provider } from 'react-redux';
import { ToastContainer } from 'react-toastify';
import { PersistGate } from 'redux-persist/integration/react';
import { ThemeProvider as StyledThemeProvider } from 'styled-components';
import Routes from './routes';
import { configureStore } from './store/configure-store';
import GlobalStyle from './styles/globalTheme';
import defaultTheme from './styles/theme/default.theme';
const { store, persistor } = configureStore();
const theme = createMuiTheme({});
function App() {
return (
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<StyledThemeProvider theme={defaultTheme}>
<PersistGate loading={null} persistor={persistor}>
<GlobalStyle />
<Routes />
<ToastContainer />
</PersistGate>
</StyledThemeProvider>
</MuiThemeProvider>
</Provider>
);
}
export default App;
routes.tsx
import React, { memo } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import NotFound from '../components/NotFound';
import Dashboard from '../pages/Dashboard';
import Login from '../pages/Login';
import Register from '../pages/Register';
import { ProtectedRoute } from './permissions-route';
function Routes() {
return (
<BrowserRouter>
<Switch>
<ProtectedRoute
path="/dashboard"
component={Dashboard}
requiredRole={['USER']}
/>
<Route exact path="/" component={Login} />
<Route exact path="/login" component={Login} />
<Route exact path="/cadastro" component={Register} />
<Route exact path="/error" component={NotFound} />
</Switch>
</BrowserRouter>
);
}
export default memo(Routes);
permission-route.tsx
import React from 'react';
import { useSelector } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
import { RootState } from '../store/configure-store';
interface Teste {
path: any;
component: any;
render?: any;
requiredRole: any;
}
export const ProtectedRoute = ({
path,
component: Component,
render,
requiredRole,
...rest
}: Teste) => {
const userRole = useSelector((state: RootState) => state.user.role);
return (
<Route
path={path}
{...rest}
render={(props) => {
if (requiredRole.includes(userRole)) {
return Component ? <Component {...props} /> : render(props);
}
return <Redirect to="/login" />;
}}
/>
);
};
export default ProtectedRoute;
What happens after redirect occurs
Instead of returning the <Redirect /> component, try window.location.href = "/login";
return (
<Route
path={path}
{...rest}
render={(props) => {
if (requiredRole.includes(userRole)) {
return Component ? <Component {...props} /> : render(props);
}
window.location.href = "/login";
}}
/>
);
This should refresh the page and make it get the proper stylings again
I had a similar issue using MUI and Router.
I found that I was importing different versions of MUI: #mui and #material-ui after replacing #material-ui components and uninstalling it, the issue was resolved.
Most likely this is an issue with conflicting styling.
I suggest checking browser DevTools to see what files/classes are overriding these styles after redirect.
I'm creating a website using React JS, React-Router-Dom, React-Redux & React-Persist.
I set up a login & sign up page with firebase. When a user logs in or signs up, I would like to have their display name in the header-component. Using my current method, I realized that when a user is logged in, the header component re-renders every time, however, I'm not too fond of that since it doesn't make navigating my website smooth. When the user is not logged in, the header component doesn't re-render.
I'm relatively new to React JS and was reading the documentation & googling other similar problems, but I can not find a solution and having a hard time approaching this. Any assistance or suggestions would be greatly appreciated!
Below are my index.js, App.js, header.component.jsx
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import "bootstrap/dist/css/bootstrap.css";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { store, persistor } from "./redux/store";
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</BrowserRouter>
</Provider>,
document.getElementById("root")
);
App.js
import React from "react";
import { Route, Switch, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import "./App.css";
import Header from "./components/header/header.component";
import Footer from "./components/footer/footer.component";
import HomePage from "./pages/homepage/homepage.page";
import SponsorsPage from "./pages/sponsors/sponsors.page";
import TeamPage from "./pages/team/team.page";
import AccountPage from "./pages/myaccount/myaccount.page"
import SignInAndSignUpPage from "./pages/sign-in-and-sign-up/sign-in-and-sign-up.page";
import { auth, createUserProfileDocument } from "./firebase/firebase.util";
import { setCurrentUser } from "./redux/user/user.action";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "./redux/user/user.selector";
class App extends React.Component {
unSubscribeFromAuth = null;
componentDidMount() {
const { setCurrentUser } = this.props;
this.unSubscribeFromAuth = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot((snapShot) => {
setCurrentUser({
id: snapShot.id,
...snapShot.data(),
});
});
}
setCurrentUser(userAuth);
});
}
componentWillUnmount() {
this.unSubscribeFromAuth();
}
render() {
return (
<div>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/sponsors" component={SponsorsPage} />
<Route exact path="/team" component={TeamPage} />
<Route exact path="/myaccount" component={AccountPage} />
<Route
exact
path="/signin"
render={() =>
this.props.currentUser ? <Redirect to="/" /> : <SignInAndSignUpPage/>
}
/>
</Switch>
<Footer />
</div>
);
}
}
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: (user) => dispatch(setCurrentUser(user)),
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
header.component.jsx
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selector";
import { auth } from "../../firebase/firebase.util";
import { ReactComponent as Logo } from "../../assets/eaglerocketry-icon.svg";
import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import "./header.style.scss";
const Header = ({ currentUser }) => (
<Navbar collapseOnSelect expand="md" bg="light" fixed="top">
<Navbar.Brand className="mr-auto">
<Logo className="logo" />
</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="pl-md-4">
<Nav.Link href="/">HOME</Nav.Link>
<Nav.Link href="/outreach">OUTREACH</Nav.Link>
<Nav.Link href="/team">TEAM</Nav.Link>
<Nav.Link href="/sponsors">SPONSORS</Nav.Link>
</Nav>
<Nav className="ml-auto">
{currentUser ? <Nav.Link href="/myaccount">{currentUser.displayName}</Nav.Link> : null}
{currentUser ? (
<Nav.Link onClick={() => auth.signOut()}>SIGN OUT</Nav.Link>
) : (
<Nav.Link href="/signin">SIGN IN</Nav.Link>
)}
</Nav>
</Navbar.Collapse>
</Navbar>
);
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
export default connect(mapStateToProps)(Header);
Every time that a prop (in this case currentUser) changes in a stateless component (like your Header component) is gonna re-render, because you are using that prop inside or your component, not only for displaying the displayName, but also to conditionally render some links ( <Nav.Link> ). So it is inevitable that a react component doesn't re-render if its props change.
being relatively new React JS I found a solution to my question.
I had to make my functional component into a class and have a local state that stores the name & use that in my render() function instead of this.props.currentUser.displayName.
class Header extends React.Component {
constructor(props){
super(props);
this.state = {
name: this.props.currentUser.displayName,
};
}
I am new to React and React Router. I have been trying to create a single page application that has a layout with navigation bars and content and a login page that does not load inside this layout. I have been searching the web and stackoverflow, but have not been able to figure this out. To create my app, I used "yarn create react-app appName" and use "yarn add" to add react router. How do I need to create my routes such that I have a login page with its own layout and a main layout where my content can be displayed.
Here is my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
Here is my App.js:
import React, { Component } from 'react';
import Main from "./Main";
import Login, {fakeAuth} from "./components/Login";
import {
Route,
NavLink,
HashRouter,
Redirect,
withRouter,
Switch,
BrowserRouter
} from "react-router-dom";
class App extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Main} />
<Route path="/login" component={Login} />
</Switch>
</BrowserRouter>
);
}
}
export default App;
Here is my Login.js:
import React from 'react';
import {
Route,
NavLink,
HashRouter,
Redirect
} from "react-router-dom";
class Login extends React.Component {
constructor() {
super();
this.state = {
redirectToReferrer: false
}
this.login = this.login.bind(this);
}
login() {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: {
pathname: '/' } }
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return (
<Redirect to={from} />
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
)
}
}
/* A fake authentication function */
export const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100);
}
};
export default Login
Here is my Main.js:
import React, { Component } from 'react';
import Navigation from './components/Navigation'
import LeftSidebar from './components/LeftSidebar';
import RightSidebar from './components/RightSidebar';
import Login, {fakeAuth} from "./components/Login";
import {
Route,
NavLink,
HashRouter,
Redirect,
withRouter,
Switch
} from "react-router-dom";
/* Home component */
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
/* Category component */
const Category = () => (
<div>
<h2>Category</h2>
</div>
)
/* Products component */
const Products = () => (
<div>
<h2>Products</h2>
</div>
)
class Main extends Component {
render() {
return (
<HashRouter>
<div className="container container-fluid">
<Navigation/>
<div className="row">
<div className="col-md-3">
<LeftSidebar/>
</div>
<div className="col-md-6">
<Route exact path="/" component={Home} />
<Route path="/category" component={Category}/>
<PrivateRoute authed={fakeAuth.isAuthenticated} path='/products' component = {Products} />
</div>
<div className="col-md-3">
<RightSidebar/>
</div>
</div>
</div>
</HashRouter>
);
}
}
const PrivateRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/login", state: { from: props.location }
}}
/>
)}
/>
);
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
export default Main;
My links to the routes are in my navigation component:
....
<ul>
<li><Link to="/">Homes</Link></li>
<li><Link to="/category">Category</Link></li>
<li><Link to="/products">Products</Link></li>
</ul>
....
When I click the Home, Category, or Products link, I want the component to load in the SPA layout in Main.js and I want Login.js to load in its own page. Right now, Home, Category, Products, and Login are loading in the main layout. What do I need to change about these routes or other part of my code to get this working?
Edit:
Added my auth code in the Main.js class.
Added Login.js class.
In addition to following some tutorials on this, I have been following the advice from this answer:
Login Page Separated From Single-page Application (SPA) in ReactJS
However, I am not quite getting it. I am not sure exactly what I need to add to make this work.
Here are images illustrating what I am trying to do:
Main.js:
main SPA layout
Login.js:
Login page
Current issue is login is loading in the main layout rather than its own page:
Do NOT want this
I have a main routes component like this
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import AdminPanel from './pages/AdminPanel/AdminPanel'
import SignIn from './pages/SignIn'
import '../assets/stylesheets/styles.scss'
const AppContent = props => <Router>
<div>
<PrivateRoute path='/' component={AdminPanel} />
<Route path='/sign-in' component={SignIn} />
</div>
</Router>
export default AppContent
Here is the PrivateRoute.jsx
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { connect } from 'react-redux'
const PrivateRoute = ({ component: Component, currentUser, ...rest }) => (
<Route
{...rest}
render={props =>
currentUser ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: '/sign-in',
state: { from: props.location }
}}
/>
)
}
/>
)
export default connect(({ currentUser }) => ({ currentUser }))(PrivateRoute)
so if currentUser is null it redirects to /sign-in which is perfectly fine. However the problem is if there is currentUser and I go to /sign-in I still get the AdminPanel component.
Here is the currentUser reducer and AdminPanel component in case needed.
import { SIGNIN, SIGNOUT } from '../actions/types'
// so here the default state is null, however if I change it to `{}` which will mean there is a user then I am not able to go to the `/sign-in`
export default (state = null, action) => {
switch (action.type) {
case SIGNIN:
return action.payload
case SIGNOUT:
return null
default:
return state
}
}
And AdminPanel
import React, { Component } from 'react'
import { Route, Switch } from 'react-router-dom'
import Box, { Container } from 'react-layout-components'
import LeftMenu from '../../components/AdminPanel/LeftMenu'
import Header from '../../components/AdminPanel/Header'
import Home from './Home'
import Dashboard from './Dashboard'
import Categories from './Categories'
import Partners from './Partners'
import Users from './Users'
import TeamMembers from './TeamMembers'
import Settings from './Settings'
import Help from './Help'
class AdminPanel extends Component {
render () {
return (
<Box>
<Container minWidth={300}>
<LeftMenu />
</Container>
<Container padding='15px' width='100%'>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/dashboard' component={Dashboard} />
<Route exact path='/categories' component={Categories} />
<Route exact path='/partners' component={Partners} />
<Route exact path='/users' component={Users} />
<Route exact path='/team-members' component={TeamMembers} />
<Route exact path='/settings' component={Settings} />
<Route exact path='/help' component={Help} />
</Switch>
<Header />
</Container>
</Box>
)
}
}
export default AdminPanel
And SignIn component is just a plain class based component
import React, { Component } from 'react'
class SignIn extends Component {
render () {
return (
<div>SignIn</div>
)
}
}
export default SignIn
I'm new in React.
I want to create an amdin app that redirects to login by default (the main menu is hidden). Using react-router v4, based on this sample the menu is already shown by default. What is the best practice to do this? Here is my current code
in index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {
BrowserRouter,
Route,
Switch
} from 'react-router-dom';
import createHistory from 'history/createBrowserHistory'
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import App from './App';
import Login from './modules/Login';
ReactDOM.render((
<BrowserRouter history={createHistory}>
<Switch>
<Route path="/login" component={Login} />
<Route path="/" component={App} />
</Switch>
</BrowserRouter>
), document.getElementById('root'));
registerServiceWorker();
in app.js
import React from 'react'
import {
BrowserRouter,
Link,
Switch,
Route,
Redirect
} from 'react-router-dom'
import { routes } from './router-config'
import Login from './modules/Login'
import fakeAuth from './Auth'
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
)
)} />
);
const App = () => (
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/protected">Protected</Link></li>
</ul>
<Switch>
{routes.map((route,index) => (
<PrivateRoute key={index} path={route.pattern} component={route.component} exact={route.exactly} />
))}
</Switch>
</div>
</BrowserRouter>
)
export default App
in login.js
import React, { Component } from 'react'
import {
Redirect
} from 'react-router-dom'
import fakeAuth from '../Auth'
import App from '../App'
class Login extends Component {
state = {
redirectToReferrer: false
}
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (fakeAuth.isAuthenticated) {
return (
<Redirect to={"/"}/>
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
)
}
}
export default Login;
i able to click login and redirect to App, but when i refresh the page when in App, the url is changed to /login but the display is still in the App. Please help. Thanks a lot
I solved this problem by creating component and load it in App
here is my code in app.js:
import React from 'react'
import {
BrowserRouter,
Link,
Switch,
Route,
Redirect,
withRouter
} from 'react-router-dom'
import { routes } from './router-config'
import Login from './modules/Login'
import fakeAuth from './Auth'
import About from './modules/About'
import Protected from './modules/Protected'
import './App.css';
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
const AuthButton = withRouter(({ history }) => (
fakeAuth.isAuthenticated ? (
<div>
Welcome! <button onClick={() => {
fakeAuth.signout(() => history.push('/login'))
}}>Sign out</button>
<ul>
<li><Link to="/about">About</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
</div>
) : (
<Redirect to={{
pathname: '/login'
}}/>
)
))
const App = () => (
<BrowserRouter>
<div>
<AuthButton/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/about" component={About}/>
<PrivateRoute path="/protected" component={Protected}/>
</div>
</BrowserRouter>
)
export default App
Login.js
import React, { Component } from 'react'
import {
Redirect
} from 'react-router-dom'
import fakeAuth from '../Auth'
class Login extends React.Component {
state = {
redirectToReferrer: false
}
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer) {
return (
<Redirect to={from}/>
)
}
return (
<div>
<p>Please Login</p>
<div>
</div>
<Button onClick={this.login} type="primary">Log in</Button>
</div>
)
}
}
export default Login;
I don't really know if it's the best way to do this.