this.props.history.push("/") isn't redirecting me to homepage

Viewed 287

I am trying to redirect this login page to home page but for some reason this.props.history.push('/') isn't redirecting it. I have a handleSubmit that is supposed to run after I press the login button. I am not really sure what is going on. Pressing the login button runs the handleSubmit but, it goes in the catch exactly at this.props.history.push('/'). Any help is appreciated.

App.js

class App extends Component {
  render() {
    return (
      <MuiThemeProvider theme={theme}>
        <Provider store={store}>
          <Router>
            {/* <Navbar /> */}
              <div className="container">
                <Routes>
                  <Route exact path="/" element={<Home/>} />
                  <Route exact path="/loginUser" element={<Login/>} />
                  <Route exact path="/createUser" element={<Signup/>} />
                </Routes>
              </div>
          </Router>
        </Provider>
      </MuiThemeProvider>
    );
  }
}

export default App;

Login.js

class Login extends Component {
    constructor(){
        super();
        this.state = {
            email: '',
            password: '',
            errors: {}
        }
    }

    handleSubmit = (event) => {
        event.preventDefault();
        const userData = {
            email: this.state.email,
            password: this.state.password
        }
        
        axios
        .post("/loginUser", userData)
        .then(res => {
            console.log(res.data);
            localStorage.setItem('FBIdToken', `Bearer ${res.data.token}`);
            this.props.history.push('/')
        })
        .catch((err) => {
            console.log("ERROR inside loginUser.js");
        })
    }
    // Combine handleEmailChange and handlePasswordChange
    handleEmailChange = (event) => {
        this.setState({
            email: event.target.value
        })
    }
    handlePasswordChange = (event) => {
        this.setState({
            password: event.target.value
        })
    }

    render() {
        const { classes } = this.props;
        return (
            <Grid container className={classes.form}>
                <Grid item sm/>
                <Grid item sm>
                    <img src={HeroLogo} alt="CompanyLogo" className={classes.logo}/>
                    <Typography variant="h2" className={classes.pageTitle}>
                        Login
                    </Typography>
                    {/* Do we need to validate the email?? */}
                    <form noValidate onSubmit={this.handleSubmit}>
                        <FormControl margin="normal" variant="outlined" sx={{width:'50ch'}}>
                            <InputLabel htmlFor="email">Email</InputLabel>
                            <OutlinedInput
                                id="email"
                                type="email"
                                value={this.state.email}
                                className={classes.textField}
                                onChange={this.handleEmailChange}
                                label="Email"
                            />
                        </FormControl>
                        <br/>
                        <FormControl margin="normal" variant="outlined" sx={{width:'25ch'}}>
                            <InputLabel htmlFor="password">Password</InputLabel>
                            <OutlinedInput
                                id="password"
                                type="password"
                                value={this.state.password}
                                className={classes.textField}
                                onChange={this.handlePasswordChange}
                                label="Password"
                            />
                        </FormControl>
                        <br/>
                        <Button 
                            type="submit"
                            variant="contained"
                            color="primary"
                            className={classes.button}
                        >
                            LOGIN
                        </Button>
                        <br/>
                        <small>Don't have an account? <Link to="/createUser">Sign up</Link></small>
                    </form>
                </Grid>
                <Grid item sm/>
            </Grid>
        )
    }
}

package.json

{
  "name": "derms-frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.12.3",
    "@material-ui/icons": "^4.11.2",
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^12.8.3",
    "axios": "^0.24.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.6",
    "react-router-dom": "^6.1.1",
    "react-scripts": "4.0.3",
    "redux": "^4.1.2",
    "redux-thunk": "^2.4.1",
    "web-vitals": "^1.1.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy": "https://north*******************************cloudfunctions.net/api"
}

1 Answers

I'm surprised you're not seeing any errors for a couple reasons:

  1. react-router-dom v6 no longer exposes a history object to use for navigation. It's replaced with a useNavigate hook that returns a navigate function.
  2. react-router-dom v6 Route components also no longer pass any route props (i.e. history, location, and match), they just don't exist. In other words, this.props.history is undefined and should throw an error when attempting to invoke the push function.

Since Login is a class component you'll need to create your own custom withRouter component to grab the navigate function and pass it to Login as a prop.

const withRouter = Component => props => {
  const navigate = useNavigate();
  return (
    <Component {...props} navigate={navigate} />
  );
};

...

class Login extends Component {
  constructor(){
    super();
    this.state = {
        email: '',
        password: '',
        errors: {}
    }
  }

  handleSubmit = (event) => {
    event.preventDefault();
    const userData = {
      email: this.state.email,
      password: this.state.password
    }
    
    axios
    .post("/loginUser", userData)
    .then(res => {
      console.log(res.data);
      localStorage.setItem('FBIdToken', `Bearer ${res.data.token}`);
      this.props.navigate('/');
    })
    .catch((err) => {
      console.log("ERROR inside loginUser.js");
    })
  }

  ...

  render() {
    ...
    return (
      ...
    )
  }
}

export default withRouter(Login);
Related