There is one need for url authentication:
import React from "react";
import { connect } from "react-redux";
import { Switch, Route, Redirect } from "react-router-dom";
...
const IndexRouter = ({ loggedIn }) => (
<Switch>
<Route
path="/"
render={() => (loggedIn ? <Redirect to="/dashboard" /> : <Login />)}
/>
<Route exact path="/dashboard" component={DashboardRouter} />
<Route exact path="/stock" component={StockRouter} />
</Switch>
);
export default connect(
state => ({
loggedIn: state.persist.loggedIn
}),
{}
)(IndexRouter);
The code means if I have not logged in, all of url are required from client will redirect to Login component. Other than that it will route to DashboardRouter.
The StockRouter is another route related with DashboardRouter.
The problem is that if I logged in. All the unspecific url (except /dashboard, /stock) I manually typed showing the /dashboard url without anything. The specific url such as /stock can show the component StockRouter directly.
You would need to write a PrivateRoute wrapper around your Route and change the order of Routes in IndexRouter, so that the Route with path / is matched at the last otherwise all routes will match / first and will not render correctly
const PrivateRoute = ({component: Component, loggedIn, ...rest }) => {
if(!loggedIn) {
return <Redirect to="/login" />
}
return <Route {...rest} component={Component}/>
}
}
}
const IndexRouter = ({ loggedIn }) => (
<Switch>
<PrivateRoute exact path="/dashboard" component={DashboardRouter} />
<PrivateRoute exact path="/stock" component={StockRouter} />
<Redirect to="/dashboard" />
</Switch>
);
For more details, check Performing Authentication on Routes with react-router-v4
Just create a history component like this :
import React from "react";
import {withRouter} from "react-router";
let globalHistory = null;
class HistoryComponent extends React.Component {
componentWillMount() {
const {history} = this.props;
globalHistory = history;
}
componentWillReceiveProps(nextProps) {
globalHistory = nextProps.history;
}
render() {
return null;
}
}
export const GlobalHistory = withRouter(HistoryComponent);
export default function gotoRoute(route) {
return globalHistory.push(route);
}
And then import into your component:
import gotoRoute from "../../history";
gotoRoute({
pathname: "/your_url_here",
state: {
id: this.state.id
}
});
And in index.js
import {GlobalHistory} from "./history";
ReactDOM.render((
<Provider store={store}>
<BrowserRouter >
<div>
<GlobalHistory/>
<App/>
</div>
</BrowserRouter>
</Provider>
), document.getElementById('root'));
Related
I'm attempting to create a private route based on the answers here, however it is not performing the redirect.
This is my private route component
import React, { Component, useContext } from "react";
import { Redirect, Route, RouteComponentProps, RouteProps } from "react-router-dom";
import { RootStoreContext } from "../stores/rootStore";
import { observer } from "mobx-react-lite";
const PrivateRoute: React.FC<RouteProps> = ({ children, ...rest }) => {
const rootStore = useContext(RootStoreContext);
const { isAuthenticated } = rootStore.msalStore;
return (
<Route
{...rest}
render={(props: RouteComponentProps<{}>) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: "/", state: { from: props.location } }} />
)
}
/>
);
};
export default observer(PrivateRoute);
And this is my App.tsx
//Imports
const App: React.FC = () => {
return (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Route exact path="/" component={LoginPage} />
<Route path="/authcallback" component={AuthCallback} />
<PrivateRoute path="/home" component={HomePage} />
</ThemeProvider>
</BrowserRouter>
</>
);
};
export default App;
When I type /home in the browser, or create a test button that navigates to home, it loads the component even though isAuthenticated = false.
I know it's not related to the value of isAuthenticated in my mobx store as changing to const [isAuthenticated, setIsAuthenticated] = useState(false); doesn't work either. I also have no other instances of routing in my app.
What could be the issue here?
use Switch (from react-router-dom) to wrap routes,
import { Switch } from "react-router-dom";
const App: React.FC = () => {
return (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Switch>
<Route exact path="/" component={LoginPage} />
<Route path="/authcallback" component={AuthCallback} />
<PrivateRoute path="/home" component={HomePage} />
</Switch>
</ThemeProvider>
</BrowserRouter>
</>
);
};
export default App;
This discussion helped me
My solution...
https://codesandbox.io/s/modest-fire-6mj3i?file=/src/PrivateRoute.tsx:0-517
import * as React from "react";
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import "./styles.css";
const Home: React.FC = () => {
return <div>Home</div>;
};
const Login: React.FC = () => {
return <div>Login</div>;
};
export default function App() {
return (
<BrowserRouter>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/">Login</Link>
</li>
</ul>
<Switch>
<Route exact path="/" component={Login} />
<PrivateRoute redirectPath="/login" path="/home" component={Home} />
</Switch>
</BrowserRouter>
);
}
import { Redirect, Route, RouteProps } from "react-router";
import React, { useState } from "react";
export interface IPrivateRouteProps extends RouteProps {
redirectPath: string;
}
const PrivateRoute: React.FC<IPrivateRouteProps> = (props) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return isAuthenticated ? (
<Route {...props} component={props.component} render={undefined} />
) : (
<Redirect to={{ pathname: props.redirectPath }} />
);
};
export default PrivateRoute;
If I click Home I get redirected to /login and the Home component is hidden
useState is only used for testing the authenticated state from within the component.
I'm trying to redirect user to /login if he isn't authenticated.
To do so, I'm checking if the user is authenticated in componentDidMount() and if he isn't I'm redirecting him to /login using the <Redirect to="/login"/> balise.
The problem is that I have this error: You should not use <Redirect> outside a <Router>, despite the fact that I am in a <BrowserRouter>
Here is my App.js
import React from 'react';
import {BrowserRouter, Route, Switch} from "react-router-dom";
import MainPage from "./pages/main-page/main-page";
import LoginPage from "./pages/login-page/login-page";
import PrivateRoute from "./components/private-route";
function App() {
return (
<BrowserRouter>
<Switch>
<PrivateRoute exact path="/" component={MainPage}/>
<Route path="/login" component={LoginPage}/>
</Switch>
</BrowserRouter>
);
}
export default App;
Here is my main-page.js
import React from 'react';
import authManager from "../../services/auth";
import Redirect from "react-router/modules/Redirect";
export default class MainPage extends React.Component{
state = {
loggedIn: false,
};
componentDidMount() {
authManager.isAuthenticate((res) => {
if (res.data)
this.setState({loggedIn: true})
}).then()
}
render() {
if (!this.state.loggedIn)
return <Redirect to='/login'/>;
return (
<div>
</div>
)
}
}
And here is my private-route.js
import { Route, Redirect } from 'react-router-dom';
import React from "react";
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
localStorage.getItem('isLoggedIn') === 'true'
? <Component {...props}/>
: <Redirect to='/login'/>
)} />
);
export default PrivateRoute;
This code for works fine, the problem is that I'm importing the <Redirect> from 'react-router/modules/Redirect' instead of using 'react-router-dom'.
To make this code work you just have to replace in main-page.js:
import Redirect from "react-router/modules/Redirect";
to
import { Redirect } from 'react-router-dom';
import { Navigate } from 'react-router-dom';
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
localStorage.getItem('isLoggedIn') === 'true'
? <Component {...props}/>
: <Navigate to="/login" />
)} />
);
I am using react js and new at learning some of the calls. I have having some issue getting onclick function to redirect to another url in the application.
I have the following code
import { Redirect } from 'react-router-dom'
in the function
redirect() {
return <Redirect to="myComponent/subComponent"/>
}
I placed a break point and nothing happens
I also tried the following
redirect() {
const myUrl='/myComponent/subComponnent'
this.props.history.push(`${myUrl}`)
}
but the history is null
any help is greatly appreciated. thanks
If you need to redirect from inside a component/function
you can use the history function as follow
const createHistory = require("history").createBrowserHistory;
Redirectpath = () => {
let history = createHistory();
history.push("/login");
let pathUrl = window.location.href;
window.location.href = pathUrl;
}
Otherwise if you are creating a middleware for protect your routes
and want to redirect to login page on error or an unauthorized access then you can :
import React from "react";
import {
BrowserRouter as Router,
Switch,
Redirect,
Route,
} from "react-router-dom";
import Login from "./components/pages/Login";
import Home from "./components/pages/Home";
function App() {
const checkAuth = () => {
try {
// To be replaced with you condition
if (false) {
return false;
}
} catch (e) {
return false;
}
return true;
}
const AuthRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
checkAuth() ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/login' }} />
)
)} />
)
return (
<div className="row">
<Router>
<Switch>
<AuthRoute exact path="/" component={Home} />
<Route exact path="/login" component={Login} />
</Switch>
</Router>
</div>
);
}
export default App;
use BrowserRouter in your index.js file.
For an example
import {BrowserRouter} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter basename={baseUrl}>
<ChildComponent />
</BrowserRouter>,
document.querySelector('#root')
);
Now history props works well.
When a user tries to view a private page(/products/1/edit), he will be redirected to the login component.
After login i want to redirect the user to the same product edit page. **The problem is that I am unable to get the location props in the component. It is returning as undefined.
** The code is as follows.
App.js
class App extends React.Component {
render() {
return (
<div className="App">
<AppRouter />
</div>
);
}
}
export default App;
AppRouter.js
import { ConnectedRouter } from 'react-router-redux';
...
class AppRouter extends Component {
componentDidMount() {
this.props.checkAlreadyLoggedIn();
}
render() {
const { token, location } = this.props;
return ((
<ConnectedRouter history={history}>
<Layout style={{ minHeight: '100vh' }}>
<Layout>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" render={ () => token && <Redirect to=
{
location.state && location.state.from ? location.state.from.pathname :
"/dashboard"
} /> || <Login />} />
<PrivateRoute path="/dashboard" component={Dashboard} />
...
</Switch>
</Layout>
</Layout>
</ConnectedRouter>
));
}
}
history.js
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
export default history;
i am already defined the private route as follows
class PrivateRoute extends PureComponent {
static propTypes = {
token: PropTypes.string,
component: PropTypes.any
}
render() {
const { component: Component, ...rest } = this.props;
let token = utils.getUserToken();
return (
<Route
{...rest}
render={ () =>
(token && <Component {...this.props} />) ||
<Redirect to={{
pathname: '/login',
state: { from: this.props.location }
}}/>
}
/>
)
}
}
Only if i get the location.state.from.pathname, i will be able to redirect the user to the page that he tried to access prior to login.
Any idea on how to fix this?
This is the PrivateRoute implementation I use and it performs redirect:
import React from 'react';
import { Route, Redirect } from "react-router-dom";
import { isLoggedIn } from '../../utils/authHelper';
class PrivateRoute extends React.Component {
renderComponent = () => {
const { path, component: Component } = this.props;
return (
isLoggedIn()
? <Component {...this.props} />
: <Redirect to={{
pathname: '/login',
state: { from: path }
}} />
)
}
render (){
const { component: Component, ...rest } = this.props;
return (
<Route {...rest} render={this.renderComponent} />
)
}
}
export default PrivateRoute;
And usage is like:
<Switch>
<Route path='/login' component={LoginScreen} />
<PrivateRoute path='/profile' component={ProfileComponent} />
</Switch>
And on /login route you will get the original url via this.props.location.state.from:
...
doLogin(username, password)
.then(() => {
window.location.href = `/${this.props.location.state.from}`;
});
Using react hooks useHistory() method history.goBack()
...
login(userData);
history.goBack();
Given an authentication token and a function checkToken how would I go about rerouting from multiple routes using the react router to prevent repetition like the below?
<Route exact path="/" render={() => {
return checkToken() ? (<Dashboard />) : (<Redirect to="/login" />)
}} />
<Route exact path="/about" render={() => {
return checkToken() ? (<About />) : (<Redirect to="/login" />)
}} />
It gets cumbersome if I have a couple dozen routes to have this repeated.
Surely there must be a better way!
Here is how I like to handle this:
Create a routers folder in src
Inside the router folder create 3 files AppRouter.js, PrivateRoute.js & PublicRoute.js
Here is your PublicRoute.js:
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
export const PublicRoute = ({ isAuthenticated, component: Component, ...rest }) => (
<Route {...rest} component={(props) => (
isAuthenticated ? <Redirect to="/dashboard" /> : <Component {...props} />
)} />
);
const mapStateToProps = state => ({
isAuthenticated: // however you need to keep track of that...
});
export default connect(mapStateToProps)(PublicRoute);
Here is your PrivateRoute.js:
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
export const PrivateRoute = ({ isAuthenticated, component: Component, ...rest }) => (
<Route {...rest} component={(props) => (
isAuthenticated ? <Component {...props} /> : <Redirect to="/" />
)} />
);
const mapStateToProps = state => ({
isAuthenticated: // however you need to keep track of that...
});
export default connect(mapStateToProps)(PrivateRoute);
And finally here is your AppRouter.js:
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Dashboard from '../components/Dashboard';
import NotFound from '../components/NotFound';
import Login from '../components/Login';
import PrivateRoute from './PrivateRoute';
import PublicRoute from './PublicRoute';
const AppRouter = () => (
<BrowserRouter>
<Switch>
{/* use PublicRoute for public routes */}
<PublicRoute exact path="/" component={Login} />
{/* use PrivateRoute for private routes */}
<PrivateRoute path="/dashboard" component={Dashboard} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
);
export default AppRouter;
For more info on HOCs (Higher Order Components) look up the docs: https://reactjs.org/docs/higher-order-components.html
One way to achieve it is by placing your checkToken function inside componentDidMount so you will always check if your user is authenticated each time this component is mounted.
After that you can do something like this:
let routes = (
<Switch>
<Route path="/login" component={LoginComponent} />
<Redirect to="/" />
</Switch>
);
if (isAuth) {
routes = (
<Switch>
<Route path="/yourRoute" component={YourComponent} />
<Redirect to="/" />
</Switch>
);
}
return (
<div>
{routes}
</div>