Browser back button can not navigate in React Router v4 - javascript

my problem is as in the title.
I am using react v16 and react-router v4
I navigate to couple of pages after that i click on the browser back button. It takes me to last visited page not the last route that i navigated.
In my previous react project (react v15, react-router v3) it is working just great.
Here is my source code, please tell me my mistake.
Thank you.
index.js
import { HashRouter as Router } from 'react-router-dom';
import routes from 'routes/index';
render(
<Provider store={store}>
<div>
<Router children={routes}/>
<ReduxToastr
timeOut={2000}
newestOnTop={false}
preventDuplicates={false}
position="top-right"
transitionIn="fadeIn"
transitionOut="fadeOut"
progressBar={false}
showCloseButton={true}/>
</div>
</Provider>, window.document.getElementById('app'));
routes.js
export default (
<Switch>
<Route path="/login" component={Login} exact/>
<Route path="/logout" component={Logout} exact/>
<PrivateRoute path="/" component={Home} exact/>
<PrivateRoute path="/home" component={Home}/>
<PrivateRoute path="/apikeylist" component={ApiKeyList}/>
<PrivateRoute path="/apikey/new" component={ApiKeyAddUpdate}/>
<PrivateRoute path="/apikey/edit/:apiKey" component={ApiKeyAddUpdate}/>
<PrivateRoute path="/etf/promoter" component={EtfPromoter}/>
<PrivateRoute path="/etf/umbrella" component={EtfUmbrella}/>
<PrivateRoute path="/etf/fund" component={EtfFund}/>
<PrivateRoute path="/etf/shareclass" component={EtfShareclass}/>
<PrivateRoute path="/index/indexvariant" component={IndexVariant}/>
</Switch>
);
PrivateRoute.js
import React from 'react';
import PropTypes from 'prop-types';
import { Route } from 'react-router';
import App from 'layout/pages/App';
import { connect } from 'react-redux';
import Login from 'layout/pages/login';
import { withRouter } from 'react-router-dom';
class PrivateRoute extends React.Component {
constructor(props) {
super(props);
}
render() {
const { authenticated, component: Component, ...rest } = this.props;
return (
<Route {...rest} render={props => (
authenticated ? (
<App>
<Component {...props}/>
</App>
) : (
<Login/>
)
)}/>
);
}
}
PrivateRoute.propTypes = {
authenticated: PropTypes.bool,
component: PropTypes.any
};
const mapStateToProps = state => {
return {
authenticated: state.auth.authenticated
};
};
export default withRouter(connect(mapStateToProps)(PrivateRoute));

Removed replace props from <NavLink/> and its works.
replace attribute replaces your route with the current one. So it never keep the whole history only one route browser can remember.
Solved.

Related

React v6-Redux Create PrivateRoutes

Im trying to create a PrivateRoutes in addition to the regular routes.
After login, the page is successfully re-direct to /home, however when I tried to open /work, the page will go back to /home. all the data from state.valid is also shows "unidentified" in /work.
I figured it out that inside the privateRoutes it check if valid.isAuthenticated is true or not. However since. valid.isAuthenticated is set to false as initial value in reducer, everytime I open /home or /work, it re-render /login and then render /home or /work.
How do I fix to not to render /login before opening other pages?
Here is my PrivateRoutes.js
import React from "react";
import { useSelector } from "react-redux";
import { Navigate } from "react-router-dom";
const PrivateRoute = ({ children }) => {
const valid = useSelector((state) => state.valid);
return valid.isAuthenticated ? children : <Navigate to="/login" />;
};
export default PrivateRoute;
here is my AppRouter.js
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import App from "../components/App";
import Login from "../components/Login";
import HomePage from "../components/HomePage";
import WorkPage from "../components/WorkPage";
const AppRouter = () => (
<BrowserRouter>
<div>
<NavigationBar />
<div>
<Routes>
<Route path="/" element={<App />} exact />
<Route path="/login" element={<Login />} exact />
<Route path="/home" element={<PrivateRoute><HomePage /></PrivateRoute>} />
<Route path="/work" element={<PrivateRoute><WorkPage /></PrivateRoute>} />
</Routes>
</div>
</div>
</BrowserRouter>
);
export default AppRouter;
useEffect(() => {
if (valid.isAuthenticated) {
navigate("/home");
}
},[valid.isAuthenticated]);
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import App from "../components/App";
import Login from "../components/Login";
import HomePage from "../components/HomePage";
import WorkPage from "../components/WorkPage";
const AppRouter = () => {
useEffect(() => {
if (!valid.isAuthenticated) {
navigate("/login");
}
});
return (
<BrowserRouter>
<div>
<NavigationBar />
<div>
{valid.isAuthenticated ? <>
<Routes>
<Route path="/" element={<App />} exact />
<Route path="/home" element={<HomePage />} />
<Route path="/work" element={<WorkPage />} />
</Routes>
</>
:
<>
<Routes>
<Route path="/login" element={<Login />} exact />
</Routes>
</>
}
</div>
</div>
</BrowserRouter>
)
};
export default AppRouter;

React Router Dom, protected route, getting ProtectedRoute is not a route

I created file auth.js file and ProtectedRoute.js in my application. Using JWT in API created a token and stored in local storage while login into my application. In my app.js imported the Authprovider and ProtectedRoute it shows error in route .please check my code and tell me where i made mistake
auth.js
import { useContext, createContext } from "react";
const AuthContext = createContext(null)
export const AuthProvider=({ children })=>{
const keyToken = localStorage.getItem("token");
const user = localStorage.getItem("name");
const userid = localStorage.getItem("id");
const pimg = localStorage.getItem("profile");
return(
<AuthContext.Provider value={{ keyToken,user,userid,pimg}}> {children}
</AuthContext.Provider>
)
}
export const useAuth = () => {
return useContext(AuthContext)
}
protectedRoute.js
import React from "react";
import { Navigate , Route } from "react-router-dom";
import {useAuth} from "./auth"
function ProtectedRoute({ component: Component, ...restOfProps }) {
const auth=useAuth();
const isAuthenticated = auth.keyToken;
console.log("this", isAuthenticated);
return (
<Route
{...restOfProps}
render={(props) =>
false ? <Component {...props} /> : <Navigate to="/login" />
}
/>
);
}
export default ProtectedRoute;
App.js
import React from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Routes,Route} from 'react-router-dom';
import Login from "./components/SignIn";
import Category from "./pages/Category";
import Addcategory from "./pages/Addcategory";
import Subcategory from "./pages/Subcategory";
import Dashboard from "./pages/Dashboard";
import { Profile } from "./components/Profile";
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { AuthProvider} from "./components/authentication/auth";
import ProtectedRoute from "./components/authentication/protectedRoute";
function App() {
return (
<AuthProvider>
<Router>
<Routes>
<Route exact path='/' element={< Login />}></Route>
<Route exact path='/login' element={< Login />}></Route>
<ProtectedRoute exact path='/dashboard' element={ Dashboard}/>
{/*<Route exact path='/dashboard' element={< Dashboard />}></Route>*/}
<Route exact path='/category' element={< Category />}></Route>
<Route exact path='/categoryAdd' element={< Addcategory />}></Route>
<Route exact path='/subcategory' element={< Subcategory />}></Route>
<Route exact path='/profile' element={< Profile />}></Route>
</Routes>
<ToastContainer />
</Router>
</AuthProvider>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
export default App;
Error showing in console:
While what you did for ProtectedRoute I think would work for React Router Dom version 5, the version 6 is slightly different. Here is one way to do it (look at this example made by the library team to know more):
App.js:
function App() {
return (
<AuthProvider>
<Router>
<Routes>
<Route exact path='/dashboard' element={ <ProtectedRoute/>}/>
</Routes>
<ToastContainer />
</Router>
</AuthProvider>
);
}
ProtectedRoute.js:
function ProtectedRoute() {
const auth=useAuth();
const isAuthenticated = auth.keyToken;
if(isAuthenticated){
return <Dashboard/>
}
return (
<Navigate to="/login" />
);
}
export default ProtectedRoute;
You have mixed code of react-router-dom v5 and v6 you can read the migrate guide upgrading from v5
Can using Outlet to render ProtectedRoute as layout. Check this example
// ProtectedRoute.js
import { Navigate, Outlet } from 'react-router-dom';
export const ProtectedRoute = () => {
const location = useLocation();
const auth = useAuth();
return auth.isAuthenticated
? <Outlet />
: <Navigate to="/login" state={{ from: location }} replace />;
};
// App.js
import { BrowserRouter, Routes, Route } from "react-router-dom";
function App() {
return (
<AuthProvider>
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/" element={<ProtectedRoute /> }>
<Route path='dashboard' element={<Dashboard />} />
<Route path='category' element={<Category />} />
// rest of your code
</Route>
</Routes>
<ToastContainer />
</BrowserRouter>
</AuthProvider>
);
}

Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>

I'm using React Router v6 and am creating private routes for my application.
In file PrivateRoute.js, I've the code
import React from 'react';
import {Route,Navigate} from "react-router-dom";
import {isauth} from 'auth'
function PrivateRoute({ element, path }) {
const authed = isauth() // isauth() returns true or false based on localStorage
const ele = authed === true ? element : <Navigate to="/Home" />;
return <Route path={path} element={ele} />;
}
export default PrivateRoute
And in file route.js I've written as:
...
<PrivateRoute exact path="/" element={<Dashboard/>}/>
<Route exact path="/home" element={<Home/>}/>
I've gone through the same example React-router Auth Example - StackBlitz, file App.tsx
Is there something I'm missing?
I ran into the same issue today and came up with the following solution based on this very helpful article by Andrew Luca
In PrivateRoute.js:
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
const PrivateRoute = () => {
const auth = null; // determine if authorized, from context or however you're doing it
// If authorized, return an outlet that will render child elements
// If not, return element that will navigate to login page
return auth ? <Outlet /> : <Navigate to="/login" />;
}
In App.js (I've left in some other pages as examples):
import './App.css';
import React, {Fragment} from 'react';
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Home from './components/pages/Home';
import Register from './components/auth/Register'
import Login from './components/auth/Login';
import PrivateRoute from './components/routing/PrivateRoute';
const App = () => {
return (
<Router>
<Fragment>
<Navbar/>
<Routes>
<Route exact path='/' element={<PrivateRoute/>}>
<Route exact path='/' element={<Home/>}/>
</Route>
<Route exact path='/register' element={<Register/>}/>
<Route exact path='/login' element={<Login/>}/>
</Routes>
</Fragment>
</Router>
);
}
In the above routing, this is the private route:
<Route exact path='/' element={<PrivateRoute/>}>
<Route exact path='/' element={<Home/>}/>
</Route>
If authorization is successful, the element will show. Otherwise, it will navigate to the login page.
Only Route components can be a child of Routes. If you follow the v6 docs then you'll see the authentication pattern is to use a wrapper component to handle the authentication check and redirect.
function RequireAuth({ children }: { children: JSX.Element }) {
let auth = useAuth();
let location = useLocation();
if (!auth.user) {
// Redirect them to the /login page, but save the current location they were
// trying to go to when they were redirected. This allows us to send them
// along to that page after they login, which is a nicer user experience
// than dropping them off on the home page.
return <Navigate to="/login" state={{ from: location }} />;
}
return children;
}
...
<Route
path="/protected"
element={
<RequireAuth>
<ProtectedPage />
</RequireAuth>
}
/>
The old v5 pattern of create custom Route components no longer works. An updated v6 pattern using your code/logic could look as follows:
const PrivateRoute = ({ children }) => {
const authed = isauth() // isauth() returns true or false based on localStorage
return authed ? children : <Navigate to="/Home" />;
}
And to use
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
Complement to reduce lines of code, make it more readable and beautiful.
This could just be a comment but I don't have enough points, so I'll
put it as an answer.
Dallin's answer works but Drew's answer is better! And just to complete Drew's answer on aesthetics, I recommend creating a private component that takes components as props instead of children.
Very basic example of private routes file/component:
import { Navigate } from 'react-router-dom';
const Private = (Component) => {
const auth = false; //your logic
return auth ? <Component /> : <Navigate to="/login" />
}
Route file example:
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/user" element={<Private Component={User} />} />
</Routes>
I know that this is not exactly the recipe on how to make PirvateRoute work, but I just wanted to mention that the new documentation recommends a slightly different approach to handle this pattern with react-router v6:
<Route path="/protected" element={<RequireAuth><ProtectedPage /></RequireAuth>} />
import { Navigate, useLocation } from "react-router";
export const RequireAuth: React.FC<{ children: JSX.Element }> = ({ children }) => {
let auth = useAuth();
let location = useLocation();
if (!auth.user) {
return <Navigate to="/login" state={{ from: location }} />;
}
return children;
};
And you are supposed to add more routes inside ProtectedPage itself if you need it.
See the documentation and an example for more details. Also, check this note by Michael Jackson that goes into some implementation details.
Just set your router component to element prop:
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
You can also check for upgrading from v5.
Remove the PrivateRoute component from your project and use the following code in your App.js files:
import {Navigate} from "react-router-dom";
import {isauth} from 'auth'
...
<Route exact path="/home" element={<Home/>}/>
<Route exact path="/" element={isauth ? <Dashboard/> : <Navigate to="/Home" />}/>
It's 2022 and I did something like below:
// routes.tsx
import { lazy } from "react";
import { Routes, Route } from "react-router-dom";
import Private from "./Private";
import Public from "./Public";
const Home = lazy(() => import("../pages/Home/Home"));
const Signin = lazy(() => import("../pages/Signin/Signin"));
export const Router = () => {
return (
<Routes>
<Route path="/" element={Private(<Home />)} />
<Route path="/signin" element={Public(<Signin />)} />
</Routes>
);
};
// Private.tsx
import { Navigate } from "react-router-dom";
import { useEffect, useState } from "react";
function render(c: JSX.Element) {
return c;
}
const Private = (Component: JSX.Element) => {
const [hasSession, setHasSession] = useState<boolean>(false);
useEffect(() => {
(async function () {
const sessionStatus = await checkLoginSession();
setHasSession(Boolean(sessionStatus));
})();
}, [hasSession, Component]);
return hasSession ? render(Component) : <Navigate to="signin" />;
};
export default Private;
Hope this helps!
React Router v6, some syntactic sugar:
{auth && (
privateRoutes.map(route =>
<Route
path={route.path}
key={route.path}
element={auth.isAuthenticated ? <route.component /> : <Navigate to={ROUTE_WELCOME_PAGE} replace />}
/>
)
)}
I tried all answers, but it always displayed the error:
Error: [PrivateRoute] is not a component. All component children of must be a or <React.Fragment>
But I found a solution ))) -
In PrivateRoute.js file:
import React from "react"; import { Navigate } from "react-router-dom";
import {isauth} from 'auth'
const PrivateRoute = ({ children }) => {
const authed = isauth()
return authed ? children : <Navigate to={"/Home" /> };
export default ProtectedRoute;
In the route.js file:
<Route
path="/"
element={
<ProtectedRoute >
<Dashboard/>
</ProtectedRoute>
}
/>
<Route exact path="/home" element={<Home/>}/>
Children of Routes need to be Route elements, so we can change the ProtectedRoute:
export type ProtectedRouteProps = {
isAuth: boolean;
authPath: string;
outlet: JSX.Element;
};
export default function ProtectedRoute({
isAuth,
authPath,
outlet,
}: ProtectedRouteProps) {
if (isAuth) {
return outlet;
} else {
return <Navigate to={{pathname: authPath}} />;
}
}
And then use it like this:
const defaultProps: Omit<ProtectedRouteProps, 'outlet'> = {
isAuth: //check if user is authenticated,
authPath: '/login',
};
return (
<div>
<Routes>
<Route path="/" element={<ProtectedRoute {...defaultProps} outlet={<HomePage />} />} />
</Routes>
</div>
);
This is the simple way to create a private route:
import React from 'react'
import { Navigate } from 'react-router-dom'
import { useAuth } from '../../context/AuthContext'
export default function PrivateRoute({ children }) {
const { currentUser } = useAuth()
if (!currentUser) {
return <Navigate to='/login' />
}
return children;
}
Now if we want to add a private route to the Dashboard component we can apply this private route as below:
<Routes>
<Route exact path="/" element={<PrivateRoute><Dashboard /></PrivateRoute>} />
</Routes>
For longer elements
<Router>
<div>
<Navbar totalItems={cart.total_items}/>
<Routes>
<Route exact path='/'>
<Route exact path='/' element={<Products products={products} onAddToCart={handleAddToCart}/>}/>
</Route>
<Route exact path='/cart'>
<Route exact path='/cart' element={<Cart cart={cart}/>}/>
</Route>
</Routes>
</div>
</Router>
Header will stay on all page
import React from 'react';
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
const Header = () => <h2>Header</h2>
const Dashboard = () => <h2>Dashboard</h2>
const SurveyNew = () => <h2>SurveyNew</h2>
const Landing = () => <h2>Landing</h2>
const App = () =>{
return (
<div>
<BrowserRouter>
<Header />
<Routes >
<Route exact path="/" element={<Landing />} />
<Route path="/surveys" element={<Dashboard />} />
<Route path="/surveys/new" element={<SurveyNew/>} />
</Routes>
</BrowserRouter>
</div>
);
};
export default App;
<Route path='/' element={<Navigate to="/search" />} />
You can use a function for a private route:
<Route exact path="/login" element={NotAuth(Login)} />
<Route exact path="/Register" element={NotAuth(Register)} />
function NotAuth(Component) {
if (isAuth)
return <Navigate to="/" />;
return <Component />;
}
I'm using "react-router-dom": "^6.3.0" and this is how I did mine
PrivateRoute Component and Route
import {Route} from "react-router-dom";
const PrivateRoute = ({ component: Compontent, authenticated }) => {
return authenticated ? <Compontent /> : <Navigate to="/" />;
}
<Route
path="/user/profile"
element={<PrivateRoute authenticated={true} component={Profile} />} />
For the error "[Navigate] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>", use the following method maybe solved:
DefaultPage is when no match router. Jump to the DefaultPage. Here use the <Route index element={} /> to replace the
<Navigate to={window.location.pathname + '/kanban'}/>
See Index Routes
<Routes>
<Route path={'/default'} element={<DefaultPage/>}/>
<Route path={'/second'} element={<SecondPage/>}/>
{/* <Navigate to={window.location.pathname + '/kanban'}/> */}
<Route index element={<DefaultPage/>} />
</Routes>
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<h1>home page</h1>} />
<Route path="/seacrch" element={<h1>seacrch page</h1>} />
</Routes>
</Router>
);
}
export default App;

How to redirect to another Page using a button in Reactjs

I am trying to learn how to redirect through pages using React.
I have tried to write some code on my own but i keep getting problems. I Created a route class for the class path, and 2 classes to move through. And the route class is imported to the app class. I am not pasting any data from the second class because its a written paragraph to be displayed.
This is what i have done:
import React from 'react'
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Firsttry from './firsttry'
import Comp2 from "./comp2";
const Routes = () => (
<Router>
<Switch>
<Route path="/" component={Firsttry} />
<Route path="/comp2" component={Comp2} />
</Switch>
</Router>
);
export default Routes;
Second class:
import React, { Component } from "react";
import { Redirect } from "react-router-dom";
class Firsttry extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false
};
}
onclick = () => {
this.setState({
redirect: true
});
};
render() {
if (this.state.redirect) {
return <Redirect to="/comp2" />;
}
return (
<div>
<p> hello</p>
<button onClick={this.onclick}>click me</button>
</div>
);
}
}
export default Firsttry;
Switch the routes. May be always your first route is getting hit and Comp2 is never rendered.
<Switch>
<Route path='/comp2' component={Comp2} />
<Route path='/' component={Firsttry}/>
</Switch>
Or you have another option: adding exact prop to your route.
<Switch>
<Route exact path='/' component={Firsttry}/>
<Route exact path='/comp2' component={Comp2} />
</Switch>
Only one Route inside a Switch can be active at a time, and / will match every route. You can add the exact prop to the / route to make sure it will only match on the root path.
const Routes = () => (
<Router>
<Switch>
<Route exact path="/" component={Firsttry} />
<Route path="/comp2" component={Comp2} />
</Switch>
</Router>
);

Cannot pass down history prop to Component

I want to pass down the history prop down from the App to the Navigation component.
When I try to do so, I get the following error message:
Failed prop type: The prop history is marked as required in Navigation, but its value is undefined.
How can I resolve this issue?
App.js:
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
const App = props => (
<Router>
<MainLayout {...props}>
<Switch>
<Route exact name="index" path="/" component={Index}/>
<Route component={NotFound}/>
</Switch>
</MainLayout>
</Router>
);
MainLayout.js:
import React from "react";
import PropTypes from "prop-types";
import Navigation from "../../components/Navigation/Navigation";
const MainLayout = props => {
const { children, authenticated, history } = props;
return (
<div>
<Navigation authenticated={authenticated} history={history} />
{children}
</div>
);
};
MainLayout.PropTypes = {
children: PropTypes.node.isRequired,
authenticated: PropTypes.bool.isRequired,
history: PropTypes.object.isRequired,
};
export default MainLayout;
SOLUTION #1:
If you simply convert <MainLayout /> to a <Route /> that renders you will have access to the history object.
<Route render={(props) =>
<MainLayout {...props}>
<Switch>
<Route exact name="index" path="/" component={Index}/>
<Route component={NotFound}/>
</Switch>
</MainLayout>
}/>
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js
<App /> does not have access to history as a prop so this will never do what you are wanting <MainLayout {...props}>
SOLUTION #2
You can also reference the history object as a single exported module in your app and refer to that both React router and any other compopent / javascript file in your app.
import { Router, Switch, Route } from 'react-router-dom';
import history from './history';
const App = props => (
<Router history={history}>
<MainLayout history={history} {...props}>
<Switch>
<Route exact name="index" path="/" component={Index}/>
<Route component={NotFound}/>
</Switch>
</MainLayout>
</Router>
);
(history.js)
import createBrowserHistory from 'history/createBrowserHistory';
export default createBrowserHistory();
https://www.npmjs.com/package/history
For react-router-dom v4
In order to get Router component's prop I used the withRouter, I guess the below change should work,
export default wihtRouter(MainLayout);
This should enable the usage of props.history in MainLayout

Categories

Resources