0

I was looking at all previous answer but none managed to solve my issue. I'm new to react and currently working on a pet project, I'm using react with typescript, redux and material UI.

For some reason trying to redirect from inside a component changes the url but doesn't render
the matching component (i.e. it defaults to my 404 page although the actual path is under the same router).

My code is as follows -
On this page I'm trying to redirect the user to projects page in case he is already logged in - the history.push on the componentDidMount here "fails".
Login.tsx page :

import React from "react";
import { bindActionCreators, Dispatch } from "redux";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import Typography from "@material-ui/core/Typography";
import {
  StyledComponentProps,
  Theme,
  withStyles,
} from "@material-ui/core/styles";
import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Checkbox from "@material-ui/core/Checkbox";
import Link from "@material-ui/core/Link";
import Grid from "@material-ui/core/Grid";
import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
import Container from "@material-ui/core/Container";
import CenteredForm from "../../components/CenteredForm";
import {
  isEmailValid,
  isPasswordValid,
} from "../../core/validators/authValidators";
import { loginUser } from "./actions";
import { history } from "../history";

interface OwnProps {
  email: string;
  password: string;
}

interface StyledProps {
  classes: StyledComponentProps & any;
}

interface DispatchProps {
  loginUser: typeof loginUser;
}

type LoginProps = OwnProps & StyledProps & DispatchProps;

interface LoginState {
  email: string;
  password: string;
  isEnabled: boolean;
}

const initialState: LoginState = {
  email: "",
  password: "",
  isEnabled: false,
};

const styles = (theme: Theme) => ({
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: "100%", // Fix IE 11 issue.
    marginTop: theme.spacing(1),
    marginBottom: theme.spacing(15),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
});

class Login extends React.Component<LoginProps, LoginState> {
  constructor(props: LoginProps, state: LoginState) {
    super(props);
    this.state = initialState;
    this.handleChange = this.handleChange.bind(this);
    this.login = this.login.bind(this);
  }

  componentDidMount() {
    if (localStorage.getItem("accessToken")) {
      history.push("/projects");
    }
  }

  // https://stackoverflow.com/questions/40676343/typescript-input-onchange-event-target-value
  // https://stackoverflow.com/a/52030057/1091286
  handleChange = (field: string) => (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    event.preventDefault();
    const newState = { ...this.state, [field]: event.target.value };
    newState.isEnabled =
      isEmailValid(newState.email) && isPasswordValid(newState.password);
    this.setState({
      ...newState,
    });
  };

  login = (event: React.MouseEvent<HTMLButtonElement>) => {
    event.preventDefault();
    if (this.state.isEnabled) {
      this.props.loginUser({
        email: this.state.email,
        password: this.state.password,
      });
    }
  };

  render() {
    const { classes } = this.props;
    return (
      <Container component="main" maxWidth="xs">
        <CssBaseline />
        <CenteredForm>
          <Avatar className={classes.avatar}>
            <LockOutlinedIcon />
          </Avatar>
          <Typography component="h1" variant="h5">
            Sign in
          </Typography>
          <form className={classes.form} noValidate>
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="email"
              onChange={this.handleChange("email")}
              label="Email Address"
              name="email"
              autoComplete="email"
              autoFocus
            />
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              name="password"
              label="Password"
              type="password"
              id="password"
              onChange={this.handleChange("password")}
              autoComplete="current-password"
            />
            <FormControlLabel
              control={<Checkbox value="remember" color="primary" />}
              label="Remember me"
            />
            <Button
              type="submit"
              fullWidth
              variant="contained"
              color="primary"
              onClick={this.login}
              disabled={!this.state.isEnabled}
              className={classes.submit}
            >
              Sign In
            </Button>
            <Grid container>
              <Grid item xs>
                <Link href="/reset-password" variant="body2">
                  Forgot password?
                </Link>
              </Grid>
              <Grid item>
                <Link href="/register" variant="body2">
                  {"Don't have an account? Sign Up"}
                </Link>
              </Grid>
            </Grid>
          </form>
        </CenteredForm>
      </Container>
    );
  }
}

function mapStateToProps(state: LoginState) {
  return {
    email: state.email,
    password: state.password,
    isEnabled: state.isEnabled,
  };
}

function mapDispatchToProps(dispatch: Dispatch) {
  return bindActionCreators({ loginUser }, dispatch);
}

const connectedLoginPage = withRouter(
  connect<LoginState, any, any, any>(
    mapStateToProps,
    mapDispatchToProps
    // @ts-ignore
  )(withStyles(styles)(Login))
);

export { connectedLoginPage as Login };


App.tsx page :
import React from "react";
import { Component } from "react";
import "./App.css";
import { Router } from "react-router";
import { Switch, Route } from "react-router-dom";
import {
  isMobile,
} from "react-device-detect";
import { history } from "../history";
import Header from "../../components/Header";
import Footer from "../../components/Footer";
import Home from "../Home";
import Login from "../Login";
import Register from "../Register";
import ForgotPassword from "../ForgotPassword";
import Projects from "../Projects";
import NotFound from "../NotFound";

class App extends Component {
  render() {
    if (isMobile) {
      return <div> This content is unavailable on mobile</div>;
    } else {
      return (
        <div className="page-container">
          <Header />
          <div className="content-wrap">
            <Router history={history}>
              <div>
                <Switch>
                  <Route exact path="/" component={Home} />
                  <Route exact path="/login" component={Login} />
                  <Route exact path="/register" component={Register} />
                  <Route
                    exact
                    path="/reset-password"
                    component={ForgotPassword}
                  />
                  <Route exact path="/projects" component={Projects} />
                  <Route component={NotFound} />
                </Switch>
              </div>
            </Router>
          </div>
          <Footer />
        </div>
      );
    }
  }
}

export default App;

index.tsx page:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { ThemeProvider } from "@material-ui/core";
import "./index.css";
import theme from "./theme";
import store from "./store";
import App from "./features/App";
import * as serviceWorker from "./serviceWorker";

ReactDOM.render(
  <React.StrictMode>
    <ThemeProvider theme={theme}>
      <Provider store={store}>
        <App />
      </Provider>
    </ThemeProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

serviceWorker.unregister();

history.ts class :

import { createBrowserHistory } from "history";

export const history = createBrowserHistory();

Can someone tell me what am I missing out?

crazyPixel
  • 2,301
  • 5
  • 24
  • 48
  • I've read it 5 times, can't see anything. I do have two questions though, 1. why do you wrap the Switch component in a div? 2. why do you import router from react-router, and not from react-router-dom as well? – tachko Aug 31 '20 at 22:22
  • Hi! regarding the div - that's a mistake, as for importing the router from react-router, the actual import was from react-router-dom, I saw one SO thread that shown the import from react-router and thought I made a mistake so changed it to react-router, how can I tarce my bug then (is there a bug even)? – crazyPixel Aug 31 '20 at 22:28
  • @tachko maybe it's the way I export my Login component? I had some error because of ts and just add the ignore flag there because of the withRouter .d.ts file remark (that it's currently problematic with ts) – crazyPixel Aug 31 '20 at 22:32
  • 1
    so when you go to logIn logged, it redirects to the correct url, but displays the NotFound Component instead of Projects. when you go to projects normally, it works? when you go to log in when logged out, it works? also restarting the server from time to time helps :) – tachko Aug 31 '20 at 22:42
  • Opening the Projects page works, when I go to Login (and am not logged in) I see the Login page, after logging in if I try to go to the Login page it will redirect to the Projects page but will show the NotFound page, no clue why, I even restrated my computer already =( – crazyPixel Aug 31 '20 at 22:47
  • 1
    I've read other people having similar issue. which npm version of history do you have? this is what I've found apparently the [history v 5.^ has a bug](https://github.com/ReactTraining/react-router/issues/7415). – tachko Aug 31 '20 at 22:59
  • 1
    @tachko DUDE THANK YOU!!!! Downgrading to history v4.10.1 solved my issue!!! YOU ROCK! (I'm literally stuck with it for the past 4 days!!) – crazyPixel Aug 31 '20 at 23:11

1 Answers1

0

Posting @tachko comment as the answer, latest history version has a bug, downgrading to V4.10.1 solved my issue (was using V5.0.0) https://github.com/ReactTraining/react-router/issues/7415

crazyPixel
  • 2,301
  • 5
  • 24
  • 48