Hello guys. I noticed one react-router problem while testing my project. There is a link which takes me to the product categories http://localhost:3000/category. It works fine but when I click a couple of times it looks like it takes there a couple of times as well. And when I press the go back button in Google Chrome it takes me that link again cause it was clicked on several times. How to prevent it?
import React from 'react';
import { BrowserRouter as Router,Route } from 'react-router-dom';
import { Container } from 'react-bootstrap';
import Header from './components/Header';
import Footer from './components/Footer';
import HomeScreen from './screens/HomeScreen';
import LoginScreen from './screens/LoginScreen';
import RegisterScreen from './screens/RegisterScreen';
import ProductScreen from './screens/ProductScreen';
import CartScreen from './screens/CartScreen';
import ProfileScreen from './screens/ProfileScreen';
import ShippingScreen from './screens/ShippingScreen';
import PaymentScreen from './screens/PaymentScreen';
import PlaceOrderScreen from './screens/PlaceOrderScreen';
import OrderScreen from './screens/OrderScreen';
import UserListScreen from './screens/UserListScreen';
import OrderListScreen from './screens/OrderListScreen';
import UserEditScreen from './screens/UserEditScreen';
import ProductEditScreen from './screens/ProductEditScreen';
import ProductListScreen from './screens/ProductListScreen';
import Powders from './screens/categories/Powders';
import Deodorants from './screens/categories/Deodorants';
import Soaps from './screens/categories/Soaps';
import Shampoos from './screens/categories/Shampoos';
import Papers from './screens/categories/Papers';
import Category from './screens/Category';
const App = () => {
return (
<Router>
<Header/>
<main className="py-3">
<Container>
<Route path="/" component={HomeScreen} exact />
<Route path="/page/:pageNumber" component={HomeScreen} exact />
<Route path="/search/:keyword/page/:pageNumber" component={HomeScreen} />
<Route path="/search/:keyword" component={HomeScreen} exact />
<Route path="/login" component={LoginScreen} />
<Route path="/shipping" component={ShippingScreen} />
<Route path="/placeorder" component={PlaceOrderScreen} />
<Route path="/order/:id" component={OrderScreen} />
<Route path="/payment" component={PaymentScreen} />
<Route path="/register" component={RegisterScreen} />
<Route path="/profile" component={ProfileScreen} />
<Route path="/admin/userlist" component={UserListScreen} />
<Route path="/admin/orderlist" component={OrderListScreen} />
<Route path="/admin/productlist" component={ProductListScreen} exact />
<Route path="/admin/productlist/:pageNumber" component={ProductListScreen} exact />
<Route path="/admin/user/:id/edit" component={UserEditScreen} />
<Route path="/admin/product/:id/edit" component={ProductEditScreen} />
<Route path="/product/:id" component={ProductScreen} />
<Route path="/cart/:id?" component={CartScreen} />
<Route path="/category/powders" component={Powders} />
<Route path="/category/deodorants" component={Deodorants} />
<Route path="/category/soaps" component={Soaps} />
<Route path="/category/shampoos" component={Shampoos} />
<Route path="/category/papers" component={Papers} />
<Route path="/category" component={Category} exact />
</Container>
</main>
<Footer/>
</Router>
);
}
export default App;
Turns out this is something a lot more people have raised questions about. I have faced it a number of times myself.
my work around solution would be to use <Link > instead since that is built to act like the HTML's <a > component.
Another method would be for you to use the history.block.
import { useHistory } from "react-router-dom";
import { useEffect } from "react";
export function useLocationBlocker() {
const history = useHistory();
useEffect(
() =>
history.block(
(location, action) =>
action !== "PUSH" ||
getLocationId(location) !== getLocationId(history.location)
),
[] // eslint-disable-line react-hooks/exhaustive-deps
);
}
function getLocationId({ pathname, search, hash }) {
return pathname + (search ? "?" + search : "") + (hash ? "#" + hash : "");
}
Or this other solution here. It will just check and ensure history is not repeated (Pun intended);
props.history.listen(location => {
lastLocation = location
})
// monkey patching to prevent pushing same url into history stack
const prevHistoryPush = props.history.push
props.history.push = (pathname, state = {}) => {
if (lastLocation === null ||
pathname !== lastLocation.pathname + lastLocation.search + lastLocation.hash ||
JSON.stringify(state) !== JSON.stringify(lastLocation.state)
) {
prevHistoryPush(pathname, state)
}
}
check out this few threads for clarity and some details;
https://github.com/ReactTraining/react-router/issues/5362
https://github.com/ReactTraining/history/issues/470#issuecomment-363449663
Related
The problem is that when I start using React.lazy, input styles are not rendering correctly. With lazy I get default Antd input styles, not mine. What's the problem and how I can fix it? You can see my code and its results in the pictures below. Thanks!
This picture shows styles that this component should render
This picture shows what styles are applied when using lazy
Code with lazy
import { lazy, Suspense } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import { useAppSelector } from '../../../hooks/redux-hooks';
import { selectCurrentUser } from '../../../store/slices/user/userSelectors';
import { Spinner } from '../../common/Spinner/Spinner';
const HomePage = lazy(() => import('../../../pages/HomePage'));
const ShopPage = lazy(() => import('../../../pages/ShopPage'));
const CheckoutPage = lazy(() => import('../../../pages/CheckoutPage'));
const AuthPage = lazy(() => import('../../../pages/AuthPage'));
export const AppRoutes = () => {
const currentUser = useAppSelector(selectCurrentUser);
return (
<Suspense fallback={<Spinner />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="shop/*" element={<ShopPage />} />
<Route path="checkout" element={<CheckoutPage />} />
<Route
path="auth"
element={currentUser ? <Navigate to="/" /> : <AuthPage />}
/>
</Routes>
</Suspense>
);
};
Code without lazy
import { Routes, Route, Navigate } from 'react-router-dom';
import { useAppSelector } from '../../../hooks/redux-hooks';
import { selectCurrentUser } from '../../../store/slices/user/userSelectors';
import HomePage from '../../../pages/HomePage';
import ShopPage from '../../../pages/ShopPage';
import AuthPage from '../../../pages/AuthPage';
import CheckoutPage from '../../../pages/CheckoutPage';
export const AppRoutes = () => {
const currentUser = useAppSelector(selectCurrentUser);
return (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="shop/*" element={<ShopPage />} />
<Route path="checkout" element={<CheckoutPage />} />
<Route
path="auth"
element={currentUser ? <Navigate to="/" /> : <AuthPage />}
/>
</Routes>
);
};
I want authenticated routes if user is not logged in the page should not be accessible like if someone enters in the url localhost.../admin/dashboard he should not be able to navigate instead he should be taken to signin page if not logged in.
I'm using react-router v6 and creating private routes for my application.
AdminRoute.js File Code is below
import React from "react";
import { Route, Navigate} from 'react-router-dom';
import { isAuthenticated } from "../helper/auth";
//props component is assigned to Component
//...rest spreading props property but reassigning it to a variable called rest
const AdminRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) =>
isAuthenticated() && isAuthenticated().role === 1 ? (
<Component {...props} />
) : (
<Navigate to = '/signin' />
)
}
/>
)
};
export default AdminRoute;
App.js File Code is below
import React from 'react';
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import Header from './Header';
import Home from './Home';
import Signup from './Signup';
import Signin from './Signin';
import ForgotPassword from './forgot-password';
import UserDashboard from './UserDashboard';
import AdminDashboard from './AdminDashboard';
import ShowroomDashboard from './ShowroomDashboard';
import AdminRoute from './AdminRoute';
import NotFound from './NotFound';
const App = () => (<BrowserRouter>
<Header />
<main>
<Routes>
<Route exact path='/' element={<Home />} />
<Route exact path='/signup' element={<Signup />} />
<Route exact path='/signin' element={<Signin />} />
<Route exact path='/forgotpassword' element={<ForgotPassword />} />
<Route exact path='/user/dashboard' element={<UserDashboard />} />
<AdminRoute exact path='/admin/dashboard' element={<AdminDashboard />} />
<Route exact path='/showroom/dashboard' element={<ShowroomDashboard />} />
<Route exact path = '*' element={<NotFound />} />
</Routes>
</main>
</BrowserRouter>
);
export default App;
react-router-dom no longer supports custom route components, preferring now component wrappers that handle the auth logic and render either the children prop or an Outlet for nested routes, or the redirect.
Wrap a single "Route" component:
import React from "react";
import { Navigate } from 'react-router-dom';
import { isAuthenticated } from "../helper/auth";
const AdminRoute = ({ children }) => {
return isAuthenticated()?.role === 1
? children
: <Navigate to='/signin' replace />;
};
...
<Route
path='/admin/dashboard'
element={(
<AuthRoute>
<AdminDashboard />
</AuthRoute>
)}
/>
Wrap nested Route components:
import React from "react";
import { Navigate, Outlet } from 'react-router-dom';
import { isAuthenticated } from "../helper/auth";
const AdminWrapper = () => {
return isAuthenticated()?.role === 1
? <Outlet />
: <Navigate to='/signin' replace />;
};
...
<Route path='/admin/dashboard/*' element={<AdminWrapper />}>
<Route index element={<AdminDashboard />} />
... any other '/admin/dashboard/*' routes ...
</Route>
The functions onEnter and onUpdate, does not work on react 4. I try different way to listen to this change but I start to turn in circles.
I would like to manage everything in my App component.
This function would allow me to close my Menu at each change of course. (isOpen) ==> False
Git of this project: https://github.com/marcdubois71450/Library-Exercise
I use react-router-dom, and react 4
import React, {Component} from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import PropTypes from 'prop-types';
import { scaleDown as Menu } from 'react-burger-menu'
import './App.css';
import Navigation from '../Navigation';
import LandingPage from '../Landing';
import SignUpPage from '../SignUp';
import SignInPage from '../SignIn';
import PasswordForgetPage from '../PasswordForget';
import HomePage from '../Home';
import AccountPage from '../Account';
import AdminPage from '../Admin';
import * as ROUTES from '../../constants/routes';
export default class App extends Component {
render() {
return (
<Router>
<div>
<Menu pageWrapId={ "page-wrap" } outerContainerId={ "outer-container" } isOpen={this.isOpen} >
<Navigation />
</Menu>
<main id="page-wrap">
<Route exact path={ROUTES.LANDING} component={LandingPage} />
<Route path={ROUTES.SIGN_UP} onEnter={this.onChangeRoute} component={SignUpPage} />
<Route path={ROUTES.SIGN_IN} onEnter={this.onChangeRoute} component={SignInPage} />
<Route path={ROUTES.PASSWORD_FORGET} onEnter={this.onChangeRoute} component={PasswordForgetPage} />
<Route path={ROUTES.HOME} onEnter={this.onChangeRoute} component={HomePage} />
<Route path={ROUTES.ACCOUNT} onEnter={this.onChangeRoute} component={AccountPage} />
<Route path={ROUTES.ADMIN} onEnter={this.onChangeRoute} component={AdminPage} />
</main>
</div>
</Router>
);
}
}
I would like to manage everything in my App component.
Use render instead of onEnter in React Router V4
Exemple from the doc:
// convenient inline rendering
<Route path="/home" render={() => <div>Home</div>}/>
// wrapping/composing
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props}/>
</FadeIn>
)}/>
)
<FadingRoute path="/cool" component={Something}/>
Render Function
I am having problems dividing my application and using several routers. I have a main router where it handles several small applications and in each mini application I want to manage its opportune routes. What am I failing?
What I want to do is when I receive the data of the request, redirect me to a new screen but I can not get it. Can anybody help me? Thank you
Example https://stackblitz.com/edit/react-c2tkgf?file=Hello.js
Routes.js
import { BrowserRouter } from 'react-router-dom'
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import { AuthenticatedRoute } from 'components/authenticated-route'
import Clients from 'components/clients'
import { Login } from 'components/login'
import Home from './Home/Home'
const Routes = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />} />
<Route exact path="/clients" component={Clients} />
<Route exact path="/login" component={Login} />} />
</Switch>
</BrowserRouter>
)
}
export default Routes
Clients.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Dashboard from './Dashboard/Dashboard'
import { Redirect, Route, Switch } from 'react-router-dom'
class Clients extends Component {
render() {
return (
<div>
<SearchCustomers />
{this.props.customer.token ? (
<div>
<Switch>
<Route path={`/clients:${this.props.customer.id}/dashboard`} component={Dashboard} />
</Switch>
<Redirect to={`/clients:${this.props.customer.id}/dashboard`} />
</div>
) : null}
</div>
)
}
}
const mapStateToProps = state => {
return {
customer: state.customer,
}
}
export default connect(mapStateToProps)(Clients)
In your Routes component you have:
<Route exact path="/clients" component={Clients} />
<Route exact path="/login" component={Login} />} />
since you have exact on there, those components will only be rendered when at exactly /clients or /login. in your built components, once you change the path, your parent component no longer renders, therefore nothing inside those components will render. remove the exact from your Routes:
<Rout path="/clients" component={Clients} />
<Rout path="/login" component={Login} />} />
I've recently started playing around with Redux and quickly came across this error message : Warning: You cannot change <Router routes>; it will be ignored (I'm also using react-router). After a little search, I understood I had to declare my routes in a variable so they wouldn't get re-rendered.
So here's my code :
Routing.js
import React from 'react';
import { Router, browserHistory, Route, IndexRoute, Redirect } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import RouterUtils from 'src/utils/js/RouterUtils';
import MainContent from 'src/common/main-content/js/MainContent';
// modules loaded lazily
import IndexPage from 'src/views/index/js/IndexPage';
import Page403 from 'src/views/403/js/403';
import Page404 from 'src/views/404/js/404';
import LoginPage from 'src/views/login/js/LoginPage';
import GetBusiness from 'src/route-fetch-components/business-get/js/GetBusiness';
import BusinessesPage from 'src/views/businesses-list/js/BusinessesPage';
import BusinessPage from 'src/views/business-details/js/BusinessPage';
import GetJobSeeker from 'src/route-fetch-components/jobseeker-get/js/GetJobSeeker';
import JobSeekersPage from 'src/views/jobseekers-list/js/JobSeekersPage';
import JobSeekerPage from 'src/views/jobseeker-details/js/JobSeekerPage';
import GetJobOffer from 'src/route-fetch-components/job-offer-get/js/GetJobOffer';
import JobOffersPage from 'src/views/job-offers-list/js/JobOffersPage';
import JobOfferPage from 'src/views/job-offer-details/js/JobOfferPage.js';
import JobOfferCreatePage from 'src/views/job-offer-create/js/JobOfferCreatePage';
const lazyLoadComponent = lazyModule =>
(location, cb) => {
lazyModule(module => {
cb(null, module.default);
});
};
const redirectTo404 = () => {
RouterUtils.redirect('/404');
};
const routes = (
<div>
<Route path="/" component={MainContent}>
<IndexRoute getComponent={lazyLoadComponent(IndexPage)} />
<Route path="businesses">
<IndexRoute getComponent={lazyLoadComponent(BusinessesPage)} />
<Route path=":user_id" getComponent={lazyLoadComponent(GetBusiness)}>
<IndexRoute getComponent={lazyLoadComponent(BusinessPage)} />
<Route path="job-offers">
<IndexRoute onEnter={() => redirectTo404()} /> // TODO: Add a component to list all job offers related to a business
<Route path=":job_offer_id" getComponent={lazyLoadComponent(GetJobOffer)}>
<IndexRoute getComponent={lazyLoadComponent(JobOfferPage)} />
</Route>
</Route>
</Route>
</Route>
<Route path="jobseekers">
<IndexRoute getComponent={lazyLoadComponent(JobSeekersPage)} />
<Route path=":user_id" getComponent={lazyLoadComponent(GetJobSeeker)}>
<IndexRoute getComponent={lazyLoadComponent(JobSeekerPage)} />
/*<Route path="applications">
<IndexRoute onEnter={() => redirectTo404()} /> // TODO: Add a component to list all applications related to a jobseeker
<Route path=":application_id" getComponent={lazyLoadComponent(JobOfferPage)} />
</Route>*/
</Route>
</Route>
<Route path="job-offers">
<IndexRoute getComponent={lazyLoadComponent(JobOffersPage)} />
<Route path="create" getComponent={lazyLoadComponent(JobOfferCreatePage)} />
</Route>
<Route path="403" getComponent={lazyLoadComponent(Page403)} />
<Route path="404" getComponent={lazyLoadComponent(Page404)} />
</Route>
<Route path="login" getComponent={lazyLoadComponent(LoginPage)} />
<Redirect from="*" to="404" />
</div>);
export default class Routing extends React.Component {
constructor(props, context) {
super(props);
this.history = syncHistoryWithStore(browserHistory, context.store);
}
render() {
return (
<Router history={this.history}>
{routes}
</Router>
);
}
}
Routing.contextTypes = {
store: React.PropTypes.object.isRequired,
};
It works perfectly fine, but I would now like to make a component ouf of my routes. So I just take the JSX code out in a new component :
Routing.js
import React from 'react';
import { Router, browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import Routes from './Routes';
const routes = <Routes />;
export default class Routing extends React.Component {
constructor(props, context) {
super(props);
this.history = syncHistoryWithStore(browserHistory, context.store);
}
render() {
return (
<Router history={this.history}>
{routes}
</Router>
);
}
}
Routing.contextTypes = {
store: React.PropTypes.object.isRequired,
};
Routes.js
import React from 'react';
import { Route, IndexRoute, Redirect } from 'react-router';
import RouterUtils from 'src/utils/js/RouterUtils';
import MainContent from 'src/common/main-content/js/MainContent';
// components loaded lazily
import IndexPage from 'src/views/index/js/IndexPage';
import Page403 from 'src/views/403/js/403';
import Page404 from 'src/views/404/js/404';
import LoginPage from 'src/views/login/js/LoginPage';
import GetBusiness from 'src/route-fetch-components/business-get/js/GetBusiness';
import BusinessesPage from 'src/views/businesses-list/js/BusinessesPage';
import BusinessPage from 'src/views/business-details/js/BusinessPage';
import GetJobSeeker from 'src/route-fetch-components/jobseeker-get/js/GetJobSeeker';
import JobSeekersPage from 'src/views/jobseekers-list/js/JobSeekersPage';
import JobSeekerPage from 'src/views/jobseeker-details/js/JobSeekerPage';
import GetJobOffer from 'src/route-fetch-components/job-offer-get/js/GetJobOffer';
import JobOffersPage from 'src/views/job-offers-list/js/JobOffersPage';
import JobOfferPage from 'src/views/job-offer-details/js/JobOfferPage.js';
import JobOfferCreatePage from 'src/views/job-offer-create/js/JobOfferCreatePage';
const lazyLoadComponent = lazyModule =>
(location, cb) => {
lazyModule(module => {
cb(null, module.default);
});
};
const redirectTo404 = () => {
RouterUtils.redirect('/404');
};
const Routes = () => (
<div>
<Route path="/" component={MainContent}>
<IndexRoute getComponent={lazyLoadComponent(IndexPage)} />
<Route path="businesses">
<IndexRoute getComponent={lazyLoadComponent(BusinessesPage)} />
<Route path=":user_id" getComponent={lazyLoadComponent(GetBusiness)}>
<IndexRoute getComponent={lazyLoadComponent(BusinessPage)} />
<Route path="job-offers">
<IndexRoute onEnter={() => redirectTo404()} /> // TODO: Add a component to list all job offers related to a business
<Route path=":job_offer_id" getComponent={lazyLoadComponent(GetJobOffer)}>
<IndexRoute getComponent={lazyLoadComponent(JobOfferPage)} />
</Route>
</Route>
</Route>
</Route>
<Route path="jobseekers">
<IndexRoute getComponent={lazyLoadComponent(JobSeekersPage)} />
<Route path=":user_id" getComponent={lazyLoadComponent(GetJobSeeker)}>
<IndexRoute getComponent={lazyLoadComponent(JobSeekerPage)} />
/*<Route path="applications">
<IndexRoute onEnter={() => redirectTo404()} /> // TODO: Add a component to list all applications related to a jobseeker
<Route path=":application_id" getComponent={lazyLoadComponent(JobOfferPage)} />
</Route>*/
</Route>
</Route>
<Route path="job-offers">
<IndexRoute getComponent={lazyLoadComponent(JobOffersPage)} />
<Route path="create" getComponent={lazyLoadComponent(JobOfferCreatePage)} />
</Route>
<Route path="403" getComponent={lazyLoadComponent(Page403)} />
<Route path="404" getComponent={lazyLoadComponent(Page404)} />
</Route>
<Route path="login" getComponent={lazyLoadComponent(LoginPage)} />
<Redirect from="*" to="404" />
</div>
);
export default Routes;
Ahhhhh... Cleaner. Except it doesn't work anymore. My page doesn't load, and I get this error :
Warning: [react-router] Location "/" did not match any routes
Now I'm wondering : what's the difference between assigning my JSX code to a var, as const routes = (<div>...</div>) and declaring it in a React Component (actually a pure function here, but I tested both)?
Thanks in advance for your time!