React router dom show a component after user signs in [duplicate] - javascript

I'm trying to write Authentication checking for my DashBoard. But the function itself is not getting called. Can anyone give me some solution for this? I'm developing in ReactJs.
This is the Route part :
<Router>
<div>
<Route exact path={"/"} component={Home} />
<Route path={"/SignUp"} component={SignUp} />
<Route path={"/SignIn"} component={SignIn} />
<Route path={"/Dashboard"} component={Dashboard} onEnter={this.requireAuth} />
</div>
</Router>
This is the function :
requireAuth (nextState, replace) {
console.log("?????????????",this.state.getToken);
if(!this.state.getToken) {
replace({pathname: '/'});
}
}

In react-router v4, you can make use of render prop to Route along with the lifecycle methods to replace the onEnter functionality existing in react-router v3.
See this answer for more details:
onEnter prop in react-router v4
However since all you want to do is authentication in the onEnter prop, you could easily create a HOC that does that
const RequireAuth = (Component) => {
return class App extends Component {
componentWillMount() {
const getToken = localStorage.getItem('token');
if(!getToken) {
this.props.history.replace({pathname: '/'});
}
}
render() {
return <Component {...this.props} />
}
}
}
export { RequireAuth }
and use it like
<Route path={"/Dashboard"} component={RequireAuth(Dashboard)}/>
Edit: In case you need to make a network call to find if the use if authenticated of not, you would write the HOC like
const RequireAuth = (Component) => {
return class App extends Component {
state = {
isAuthenticated: false,
isLoading: true
}
componentDidMount() {
AuthCall().then(() => {
this.setState({isAuthenticated: true, isLoading: false});
}).catch(() => {
this.setState({isLoading: false});
})
}
render() {
const { isAuthenticated, isLoading } = this.state;
if(isLoading) {
return <div>Loading...</div>
}
if(!isAuthenticated) {
return <Redirect to="/login" />
}
return <Component {...this.props} />
}
}
}
export { RequireAuth }
Update:
In addition to the HOC, you can also go for the PrivateRoute component like
const PrivateRoute = ({component: Component, isAuthenticated, isLoading, ...rest }) => {
if(isLoading) {
return <div>Loading...</div>
}
if(!isAuthenticated) {
return <Redirect to="/login" />
}
return <Component {...this.props} />
}
}
}
export { PrivateRoute };
and you can use it like
class App extends Component {
state = {
isAuthenticated: false,
isLoading: true
}
componentDidMount() {
AuthCall().then(() => {
this.setState({isAuthenticated: true, isLoading: false});
}).catch(() => {
this.setState({isLoading: false});
})
}
render() {
<Router>
<div>
<Route exact path={"/"} component={Home} />
<Route path={"/SignUp"} component={SignUp} />
<Route path={"/SignIn"} component={SignIn} />
<PrivateRoute path={"/Dashboard"} component={Dashboard} isAuthenticated={this.state.isAuthenticated} isLoading={this.isLoading}/>
</div>
</Router>
}
}

Related

Proper way to pass down state to another component

I'm adding some authorization checks to different pages in an app, but running into some errors passing down a piece of state.
I have
const RequireAuth = Component => {
return class App extends React.Component {
state = {
isAuthenticated: false,
isLoading: true
};
_updateLogin = () => {
this.setState({ isAuthenticated: true, isLoading: false });
};
render() {
const { isAuthenticated, isLoading } = this.state;
if (isLoading) {
return <div>Placeholder for animation</div>;
}
if (!isAuthenticated) {
return <Redirect to="/auth/login-page" />;
}
return <Component {...this.props} />;
}
};
};
And I am trying to pass down the _updateLogin function to the AdminLayout component, where I have a login page.
ReactDOM.render(
<Router history={hist}>
<Switch>
<Route
path="/admin"
component={RequireAuth(AdminLayout)}
login={() => {this._updateLogin}}
/>
<Route path="/auth" component={AuthLayout} />
<Redirect from="/" to="/admin/dashboard" />
</Switch>
</Router>,
document.getElementById("root")
);
If I run the above code, I get
Line 43:23: Expected an assignment or function call and instead saw an expression no-unused-expressions
If I change the login to
login={this._updateLogin}
it returns
"Cannot read property '_updateLogin' of undefined.
Any tips?

React async Logincheck with PrivateRoute

I want to make a PrivateRoute in React with an async LoginCheck. I have tried this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loggedIn: false
}
}
componentDidMount() {
//ASYNC FUNCTION
setTimeout(() => {
this.setState({loggedIn: true});
}, 1000)
}
render() {
console.log("RENDERED", this.state.loggedIn);
const PrivateRoute = ({component: Component, ...rest}) => (
<Route {...rest} render={(props) => (
this.state.loggedIn === true
? <Component {...props} />
: <Redirect to="/login" />
)}/>
)
return(
<Router>
<Switch>
<Route path="/" exact component={Home} />
<PrivateRoute path="/private" component={PrivatePage} />
</Switch>
</Router>
)
}
}
But that is not working.
In the console it shows first "RENDERED false" and after a second "RENDERED true". So it is rendered with the right parameters but in the const PrivateRoute he is redirecting to the login page so loggedIn is false.
Why? What can I do?
You can create a function checkAuth() to seperate the validation as follow
import React from "react";
import {
BrowserRouter as Router,
Switch,
Redirect,
Route,
} from "react-router-dom";
import Home from "./Home";
import PrivatePage from "./Private";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loggedIn: true
}
}
componentDidMount() {
//ASYNC FUNCTION
setTimeout(() => {
this.setState({ loggedIn: false });
}, 1000)
}
render() {
console.log("RENDERED", this.state.loggedIn);
const checkAuth = () => {
try {
// To be replaced with your condition
if (this.state.loggedIn === true) {
return true;
}
} catch (e) {
return false;
}
return false;
}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
checkAuth() ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/' }} />
)
)} />
)
return (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<PrivateRoute path="/private" component={PrivatePage} />
</Switch>
</Router>
)
}
}
Visit the url "http://localhost/private" as loggedIn: true this route is authorized but after 0.2s the state of loggedIn will be update it false and the protected route will be redirected to the home page.

What is best option to guard your routes in React?

I am using this HOC to guard my routes but I find odd using this HOC in every component because I am already using 1 or 2 HOC's like reduxForm etc
import React from "react";
import { connect } from "react-redux";
export default ChildComponent => {
class ComposedComponent extends React.Component {
componentDidMount() {
this.shouldNavigateAway();
}
componentDidUpdate() {
this.shouldNavigateAway();
}
shouldNavigateAway() {
if (!this.props.auth) {
this.props.history.push("/");
}
}
render() {
return <ChildComponent {...this.props} />;
}
}
const mapStateToProps = state => {
return { auth: state.auth };
};
return connect(mapStateToProps)(ComposedComponent);
};
The HoC approach is right, but you should apply it to routes, not components.
Take a look at the pattern used in redux-auth-wrapper
I do not know how you implement your routes but there is clean solution for this.
render() {
let routes = (
<Switch>
<Route path="/auth" component={asyncAuth} />
<Route path="/" exact component={BurgerBuilder} />
<Redirect to="/" />
</Switch>
);
if (this.props.isAuthenticated) {
routes = (
<Switch>
<Route path="/checkout" component={asyncCheckout} />
<Route path="/orders" component={asyncOrders} />
<Route path="/logout" component={Logout} />
<Route path="/auth" component={asyncAuth} />
<Route path="/" exact component={BurgerBuilder} />
<Redirect to="/" />
</Switch>
);
}
return (
<div>
<Layout>
{routes}
</Layout>
</div>
);
}
And store the auth token in your redux.
const mapStateToProps = state => {
return {
isAuthenticated: state.auth.token !== null
};
};

Unable to implement auth routes

I'm trying to learn react, and am setting up routes in my application which require you to log in. I'm trying to adapt the example given here
The code I've written should either redirect the user or display the protected route. But when I log in I'm still being redirected.
I believe the issue is in my PrivateRoute class below. I pass it a authenticated property which is set in the parent class, but it doesn't appear to update.
In app.js we declare the authenticator, where we perform async login with our backend.
I pass the checkLoggedIn function to the login component, where we set the parent's authenticated state property to true. I'm console.log()ing the state just to check it's occurring, which it is.
When I then click the Link to /protected route I'm still being redirected.
app.js
// imports ...
let authenticator = new Authenticator();
class ProtectedComponent extends Component {
render() {
return (
<h1>Protected!</h1>
);
}
}
class App extends Component {
constructor(props){
super(props);
this.state = {
authenticator: authenticator,
authenticated: authenticator.isLoggedIn(),
}
}
checkLoggedIn() {
this.setState({authenticated: true});
console.log(this.state);
}
render() {
let routes, links = null;
links = <div className="links">
<Link to="/login">Login</Link>
<Link to="/protected">Protected</Link>
</div>;
routes = <div className="routes">
<Route
path="/login"
render={() =>
<Login
authenticator={this.state.authenticator}
loginCallback={this.checkLoggedIn} />
}
/>
<PrivateRoute
path="/protected"
component={ProtectedComponent}
authenticated={this.state.authenticated}
/>
</div>;
return (
<Router className="App">
{links}
{routes}
</Router>
);
}
}
export default App;
PrivateRoute.js
// imports ....
const PrivateRoute = ({ component: Component, authenticated, ...rest }) => (
<Route {...rest} render={props =>
authenticated === true
? (<Component {...props} />)
: (<Redirect to={{
pathname: "/login",
state: { from: props.location }
}} />
)
}/>
);
export default PrivateRoute;
Login.js
// imports ...
class Login extends Component {
constructor(props) {
super(props);
this.authenticator = props.authenticator;
this.loginCallback = props.loginCallback;
this.state = {
identifier: "",
password: "",
}
}
updateState = (e, keyName = null) => {
this.setState({[keyName]: e.target.value})
}
attemptLogin = (e) => {
this.authenticator.loginPromise(this.state.identifier, this.state.password)
.then(resp => {
if(resp.data.success === true) {
this.authenticator.setToken(resp.data.api_token);
this.loginCallback();
} else {
this.authenticator.removeToken()
}
})
.catch(err => {
console.error(err);
});
}
render(){
<button onClick={this.attemptLogin}> Log In </button>
}
}
export default Login;
I'm setting the authenticated state to true in the callback method, but when I go to the protected route (and run it's render method) it appears to be evaluating to false.
If I'm misunderstanding the react props system, let me know. If you'd like to see any more of the code let me know and I'll amend the question.
You have to create a PrivateRoute HOC component first:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
localStorage.getItem('bpm-user')
? <Component {...props} />
: <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)} />
)
and wrap your routes that most be protected:
<Switch>
<Route path="/login" component={Login} />
<PrivateRoute path="/new/index" component={NewIndex} />
<PrivateRoute path="/jobs/index" component={JobsIndex} />
<PrivateRoute path="/unions/index" component={UnionsIndex} />
<PrivateRoute exact path="/" component={ListIndex} />
<PrivateRoute exact path="/charges" component={MunicipalCharges} />
</Switch>
and use Link
<Link to="/jobs/index">Jobs</Link>
my login reducer
import axios from 'axios';
import * as actionTypes from './AuthActionTypes';
export const login = (user) => {
return dispatch => {
// for example => dispatch({type:actionTypes.REQUEST_LOGIN_USER});
axios({
method: 'post',
url: '/api/auth/login',
data: { 'username': user.username, 'password': user.password },
headers: { 'Content-Type': 'application/json;charset=utf-8' }
})
.then(res => {
localStorage.setItem('bpm-user', JSON.stringify(res.data));
dispatch({
type: actionTypes.LOGIN_USER,
payload: res.data
})
})
.catch(error => {
// TODO... for example => dispatch({type:actionTypes.FAILD_LOGIN_USER, payload:error});
})
}
}
export const logout = () => {
localStorage.removeItem('bpm-user');
}
like the example codes that i copied from my own project

React Router rendering blank pages with React Redux

In my App.js, I have the following:
const Index = asyncRoute(() => import('~/pages/index'))
const Register = asyncRoute(() => import('~/pages/register'))
const AddDesign = asyncRoute(() => import('~/pages/add-design'))
const Login = asyncRoute(() => import('~/pages/login'))
class App extends Component {
render() {
const { isLoggedIn } = this.props;
if(!isLoggedIn){
return (
<Switch>
<Route path={'/login'} component={Login} />
<Route path={'/register'} component={Register} />
<Redirect to={'/login'} />
</Switch>
);
}
return (
<Switch>
<Route exact path='/' component={Index}/>
<Route exact path='/add-design' component={AddDesign}/>
<Route exact path="/login" render={() => <Redirect to="/"/>} />
<Route exact path="/register" render={() => <Redirect to="/"/>} />
<Redirect to={'/'} />
</Switch>
);
}
}
function mapStateToProps({ user }) {
return {
isLoggedIn: !!user.token,
};
}
export default connect(mapStateToProps)(App);
When the user logs in, isLoggedIn is set to true and it then attempts to redirect the user back to "/"
This happens, however the page loaded is the index.html file within public, rather than the Index component.
I'm not sure if its making a difference, but my asyncRoute is a workaround for FOUC:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Loading from '~/components/Loading'
class AsyncImport extends Component {
static propTypes = {
load: PropTypes.func.isRequired,
children: PropTypes.node.isRequired
}
state = {
component: null
}
_hasClass(target, className) {
return new RegExp('(\\s|^)' + className + '(\\s|$)').test(target.className);
}
toggleFoucClass () {
const root = document.getElementById('root')
if (this._hasClass(root, 'fouc')) {
root.classList.remove('fouc')
} else {
root.classList.add('fouc');
}
}
componentWillMount () {
this.toggleFoucClass()
}
componentDidMount () {
this.props.load()
.then((component) => {
setTimeout(() => this.toggleFoucClass(), 1)
this.setState(() => ({
component: component.default
}))
})
}
render () {
return this.props.children(this.state.component)
}
}
const asyncRoute = (importFunc) =>
(props) => (
<AsyncImport load={importFunc}>
{(Component) => {
return Component === null
? <Loading size="large" />
: <Component {...props} />
}}
</AsyncImport>
)
export default asyncRoute
Can anyone explain why my users are being routed but the component not rendering?
Assuming using RR4
Try:
<Route path="/" component={App}>
<IndexRedirect to="/index" />
<Route path="index" component={Index} />
Refer to the following to get your usecase correct:
https://github.com/ReactTraining/react-router/blob/5e69b23a369b7dbcb9afc6cdca9bf2dcf07ad432/docs/guides/IndexRoutes.md

Categories

Resources