2

I have a login form which on click calls the following function.

Which summarizing , gets a response from the api, if valid, sets a cookie, and then redirects using the this.props.history.push()

handleSubmit(event) {
    event.preventDefault();
    const { email, password } = this.state;
    axios({
        method: 'post',
        url: 'http://localhost:3003/login',
        data: qs.stringify({ email, password }),
        headers: {
            'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
        }
    }).then(res => {
        console.log("set cookie")
        //the accestoken is set as a cookie in order to check routes
        Cookies.set('accesstoken', res.data.accesstoken);
        console.log("those are the props")
        console.log(this.props);

        this.props.history.push('/'); //the bug


    }).catch(err => {
        console.log(err)
    })  


}

But the problem im facing is that when I do the first login, the redirect wont work. It sets all the cookies and all that, but it never actually redirects the user to the desired directory. So im forced to redirect myself by typing my desired route in the browser search bar.

This means that once i force myself into the desired directory, if I logout (basically deleting cookies), and try to login again. This time the redirect works.

It will work up until I clear all my cache with ctrl+F5 which happens to cause the same issue I had in the first login, so I had to redirect myself manuall again.

EDIT: This is how my routes look

<BrowserRouter>
                <Switch>

                    <Route exact path="/login" render={(props) => <LoginPage {...props}/>} />

                    <PrivateRoute authed={this.state.isAuthenticated} exact path="/" render={(props) => <RegisterPage />} />

                </Switch>
            </BrowserRouter>

And those are my private routes

import { Route } from 'react-router-dom';

import React from 'react'; import { Redirect } from 'react-router';

export default ({ component: Component, render: renderFn, authed,  ...rest }) =>{
  //The privateroute is fed with the auth state of app.js and evaluates the render based on that . If flase always renders "/"
  if (Component){
    return (
      <Route
      {...rest}
      render={props =>
        authed === true ? (
          <Component {...props} />
        ) : (
            <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
          )
      }
    />
    )
  } else {
    return ( //Second case is for iframe based renders
      <Route {...rest} render={props => authed === true ? renderFn(props) : <Redirect to={{ pathname: '/login', state: { from: props.location } }} /> } /> 
      );
  }
}

EDIT2:

app.js

    constructor(props) {
        super(props);

        console.log("PROPS APPJS")
        console.log(props)

        //checks if user is autheticated within the system in order to manage routes
        this.state = {
            authenticationChecked: false,
            isAuthenticated: false 

        }  



    }


    componentDidMount() {
        //calls the auth service to decide the auth state value
        isAuthenticated().then((result) => {
            if (result === true) {
                this.setState({ isAuthenticated: true, authenticationChecked: true})
            } else {
                this.setState({ isAuthenticated: false, authenticationChecked: true})
            }
        });
    }

    login = (email, password) => {
        var thiscomponent  = this;
        axios({
            method: 'post',
            url: 'http://localhost:3003/login',
            data: qs.stringify({ email, password }),
            headers: {
                'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
            }
        }).then(res => {
            console.log("set cookie")
            //the accestoken is set as a cookie in order to check routes
            Cookies.set('accesstoken', res.data.accesstoken);
            console.log("those are the props")
            console.log(this.props);
            this.setState({ isAuthenticated: true }, () => {
               thiscomponent.props.history.push('/'); //the bug
            })

        }).catch(err => {
            console.log(err)
        })  
    }

.
.
.
.
.
.
.
<BrowserRouter>
                <Switch>

                    <PrivateRoute authed={this.state.isAuthenticated} exact path="/" render={(props) => <NewLandingPage login={this.login} {...props} exact />} />
                </Switch>
            </BrowserRouter>

Login page

 handleSubmit(event) {
        const { email, password } = this.state;
        this.props.login(email, password)
        event.preventDefault();


    }

EDIT: login page props

  {"history":{"length":15,"action":"POP","location":{"pathname":"/login","search":"?email=test4%40test.com&password=test","hash":""}},"location":{"pathname":"/login","search":"?email=test4%40test.com&password=test","hash":""},"match":{"path":"/login","url":"/login","isExact":true,"params":{}}}
mouchin777
  • 686
  • 8
  • 26
  • What's happening in the console when you the `push()` function is executed? – Praveen Kumar Purushothaman Jan 22 '20 at 12:26
  • Could you show how your routes are setup? – lehm.ro Jan 22 '20 at 12:28
  • @lehm.ro edited the question – mouchin777 Jan 22 '20 at 12:30
  • @PraveenKumarPurushothaman I was using the logs to check what were the props rendering. Actually when i first load the login page I can see the the props rendered by the constructor, then , when i click the function, i can see some props (not the ones rendered by the constructor) , and then suddenly the page reloads and i see the props rendered by the constructor again – mouchin777 Jan 22 '20 at 12:31
  • Where and when do you set isAuthenticated? – lehm.ro Jan 22 '20 at 12:32
  • @lehm.ro in my app.js during the componentDidMount(), default isAuthenticated is false – mouchin777 Jan 22 '20 at 12:33
  • And then once you redirect you have to use this.setState to make it true, otherwise your private route won't show. But for that you need to pass the true state up to app.js and then trigger a function to setState for isAuthenticated true, then redirect to that route – lehm.ro Jan 22 '20 at 12:34
  • 1
    I'll write a response for that... – lehm.ro Jan 22 '20 at 12:35

1 Answers1

1

Where and when do you set isAuthenticated? – lehm.ro 7 mins ago
@lehm.ro in my app.js during the componentDidMount(), default isAuthenticated is false – mouchin777 6 mins ago And then once you redirect you have to use this.setState to make it true, otherwise your private route won't show. But for that you need to pass the true state up to app.js and then trigger a function to setState for isAuthenticated true, then redirect to that route

Setup your history to work in app.js without being passed by props: Uncaught TypeError: Cannot read property 'push' of undefined (React-Router-Dom)

In order to make use of history in the App component use it with withRouter. You need to make use of withRouter only when your component is not receiving the Router props,

This may happen in cases when your component is a nested child of a component rendered by the Router or you haven't passed the Router props to it or when the component is not linked to the Router at all and is rendered as a separate component from the Routes.

import React from 'react';
import { Route , withRouter} from 'react-router-dom';
import Dashboard from './Dashboard';
import Bldgs from './Bldgs';

var selectedTab;

class App extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    selectedTab = 0;
  }

  handleClick(value) {
    selectedTab = value;
    // console.log(selectedTab);
    this.props.history.push('/Bldgs');
    // console.log(this.props);
  }

  render() {
    var _this = this;

    return (
      <div>
        <Route exact path="/" render={(props) => <Dashboard {...props} handleClick={_this.handleClick} />} />
        <Route path="/Bldgs" component={Bldgs} curTab={selectedTab} />
      </div>
    );
  }
}

export default withRouter(App);

[Documentation][1] on withRouter

[1]: https://reacttraining.com/react-router/web/api/withRouter

Change in App.js

<Route exact path="/login" render={(props) => <LoginPage login={this.login} {...props}   />} />

and add

login = (email, password) => {
    var thiscomponent = this;
    axios({
        method: 'post',
        url: 'http://localhost:3003/login',
        data: qs.stringify({ email, password }),
        headers: {
            'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
        }
    }).then(res => {
        console.log("set cookie")
        //the accestoken is set as a cookie in order to check routes
        Cookies.set('accesstoken', res.data.accesstoken);
        console.log("those are the props")
        console.log(this.props);
        this.setState({ isAuthenticated: true }, () => {
           thiscomponent.props.history.push('/'); //the bug
        })

    }).catch(err => {
        console.log(err)
    })  
}

and in LoginPage

handleSubmit(event) {
    const { email, password } = this.state;
    this.props.login(email, password)
    event.preventDefault();
}
lehm.ro
  • 683
  • 2
  • 16
  • 1
    Didn't run it..but should work. If not let me know I'll go over it again :) – lehm.ro Jan 22 '20 at 12:40
  • VM346 main.chunk.js:498 Uncaught TypeError: this.props.login is not a function at LoginPage.handleSubmit (VM346 main.chunk.js:498) trying to solve this rn – mouchin777 Jan 22 '20 at 12:48
  • make sure the login arrow function is in the same component as your routes. – lehm.ro Jan 22 '20 at 12:53
  • It is, but im afraid so far im unable to make it work, let me make an edit with my current code-details, 5 minutes – mouchin777 Jan 22 '20 at 12:55
  • I edited it with my current code, sofar it gives me the same error as the comment thtat i posted before – mouchin777 Jan 22 '20 at 12:57
  • Could you put props in console.log of constructor in Login? – lehm.ro Jan 22 '20 at 13:02
  • login={this.login} shouldn't be in props of newlandingpage but in props of LoginPage component – lehm.ro Jan 22 '20 at 13:08
  • TypeError: Cannot read property 'push' of undefined ```this.setState({ isAuthenticated: true }, () => { > 65 | thiscomponent.props.history.push('/'); //the bug | ^ 66 | })``` – mouchin777 Jan 22 '20 at 13:12
  • did you add thiscomponent = this; at the top of the function? – lehm.ro Jan 22 '20 at 13:15
  • Yes , check my second edit, but I had to add ```var thiscomponent = this;``` instead, or else the app would crash because thiscomponent was undefined – mouchin777 Jan 22 '20 at 13:16
  • 1
    yes, that was just a typo on my end. To fix your history, so we don't duplicate, have a look at this thread: https://stackoverflow.com/questions/44009618/uncaught-typeerror-cannot-read-property-push-of-undefined-react-router-dom – lehm.ro Jan 22 '20 at 13:22
  • Getting some issues implementing withRouter like ```Error: Invariant failed: You should not use outside a ``` but ill check it in another question if I dont manage to fix it. Thanks for your help so far. – mouchin777 Jan 22 '20 at 13:33
  • 2
    Once your router/history works this will work properly. Good luck on the next problem. Debugging is life! :D – lehm.ro Jan 22 '20 at 13:35