0

I'm actually working on my very first React app and I'm struggling with the redirect after login.

The project has been created with create-react-app v.2, I use mobx to handle the state management, and I'm trying to use react-router to navigate through the app.

Here is my code and what I have done so far :

Index.js :

ReactDOM.render((<Provider loginBusinessStore={new LoginBusinessStore()} > 
                        <BrowserRouter> 
                                <App /> 
                        </BrowserRouter> 
                </Provider>), document.getElementById('root'));

App.js:

class App extends Component {
  render() {

    return (

      <div className="container-fluid" >

        <Switch>
          <Route exact path='/' component={Home} />
          <Route path='/contact' component={Contact} />
          <Route path='/about' component={About} />
        </Switch>

      </div>
    );
  }
}

export default App;

Home.js:

class Home extends Component {
  render() {
    return (
      < div className="bg">
      <div className="row" >
        <div className="col-sm-4"> <TextHome /> </div>
        <div className="col-sm-4"> <Login /></div>
        <div className="col-sm-4"> </div>
      </div>
    </div>
    );
  }
}

export default Home;

In Login.js, I'm injecting an instance of LoginBusinessStore, where I have my business logic. As you see when I send my credentials, it triggers the method sendCredentials of LoginBusinessStore:

class Login extends Component {

    onChangePassword = (event) => { this.props.loginBusinessStore.setPassword(event.target.value) }
    onChangeEmail = (event) => { this.props.loginBusinessStore.setEmail(event.target.value) };

    displayErrorMessage = () => {
        if (this.props.loginBusinessStore.loginRequestStatus == "ERROR") {
            return (<p>Oups, nous avons un problème de connexion. Rééssaie plus tard. </p>)
        } if (this.props.loginBusinessStore.loginRequestStatus == "WRONG_CREDENTIALS") {
            return (<p>La combinaison email/ mot de passe n'est pas correcte.</p>)
        } 
    }


    render() {
        return (
            <div>
                <div className="form-group">
                    <label htmlFor="email">Email address</label>
                    <input type="email" onChange={this.onChangeEmail} className="form-control" id="email" aria-describedby="emailHelp" />
                    <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
                </div>

                <div className="form-group">
                    <label htmlFor="password">Password</label>
                    <input type="password" onChange={this.onChangePassword} className="form-control" id="password" />
                </div>

                <button onClick={this.props.loginBusinessStore.sendCredentials} className="btn btn-primary">Go!</button>

                <p>Créer un compte</p>
                <br />

                <div className= "error"> {this.displayErrorMessage()} </div>

            </div>
        );
    }
}



export default inject("loginBusinessStore")(observer(Login));
class LoginBusinessStore {
    email;
    password;
    token = "noTokenYet";
    loginRequestStatus = "PENDING"; // "OK", "WRONG_CREDENTIALS", "ERROR"

    setToken(myToken) {
        this.token = myToken;
    };

    setEmail(myEmail) {
        this.email = myEmail;
    };

    setPassword(myPassword) {
        this.password = myPassword;
    };

    setLoginRequestStatus(myLoginRequestStatus) {
        this.loginRequestStatus = myLoginRequestStatus;
    }

    sendCredentials = () => {
        console.log("enter sendCredential");
        const axios = require("axios");
        axios.post("http://localhost:8080/authenticate/",
            { username: this.email, password: this.password }).
            then(res => {
                console.log(res.data);
                this.setToken(res.data.token);
                this.setLoginRequestStatus("OK");
                this.props.history.push("/contact");
                // window.location.replace("/contact");


            }).catch(error => {
                if (error.message.includes("401")) {
                    console.error('Combinaison email/mdp non valide', error)
                    this.setLoginRequestStatus("WRONG_CREDENTIALS");
                } else {
                    console.error('Login impossible', error)
                    this.setLoginRequestStatus("ERROR");
                }
            })
    };
}

decorate(LoginBusinessStore, {
    loginRequestStatus: observable,
    sendCredentials: action
})


export default LoginBusinessStore;

What I'd like to do is to redirect to a specific route if the login has been successfull. I tried with this.props.history.push("/contact"); but get an error:

Cannot read property 'history' of undefined

If I use instead window.location.replace("/contact"); it does the job. But I'm not sure it's a good practice. Can someone advise me on how to handle this situation?

Julien Berthoud
  • 721
  • 8
  • 24

1 Answers1

1

You will need to pass the history props through your component tree in order to access them in LoginBusinessStore.

In your Home component you will need to pass the history props to your Login component:

<Login history={this.props.history} />

Then in your Login component, you will need to pass the history object into your sendCredentials function:

<button onClick={() => this.props.loginBusinessStore.sendCredentials(this.props.history)} className="btn btn-primary">Go!</button>

Lastly, in LoginBusinessStore, you need to adjust your function to accept the history object and use it:

sendCredentials = (history) => {

Adjust the line attempting to redirect to:

history.push("/contact");

EDIT: Updated to include the correct format for the event handler.

seanulus
  • 885
  • 4
  • 8
  • Thanks for your answer, it was very helpful. Although, passing the argument in Login as you suggested didn't work. Instead I had to write : Do you understand why is this so? – Julien Berthoud Dec 16 '19 at 21:37
  • I found this on how to pass arguments in react event handlers: https://www.w3schools.com/react/react_events.asp – Julien Berthoud Dec 16 '19 at 21:44
  • Last question: I'm glad it now works as expected, but could I have reached so same result more easily? I read about the "withRouter", is it something that could help in this situation? – Julien Berthoud Dec 16 '19 at 21:50
  • `withRouter()` could work as well. I'm not sure if you'd be able to attach it to `LoginBusinessStore` like the other answer or if you'd need to place it in your `Login` component. I've mostly used redux for state management. Not as familiar with mobx. That being said, since your application is relatively small, using prop drilling works alright. If the app becomes larger, you might begin to consider other solutions. – seanulus Dec 16 '19 at 21:54
  • And writing ```window.location.replace("/contact")``` for a redirect is a bad practice? – Julien Berthoud Dec 16 '19 at 22:02
  • It is preferred that if you are using React, you should use the React Router library alongside it, as it is designed to play nicely with React. – seanulus Dec 16 '19 at 23:29