【问题标题】:React with typescript - history.push changes the URL but renders the 404 page instead of the right page使用 typescript 做出反应 - history.push 更改 URL,但呈现 404 页面而不是正确的页面
【发布时间】:2020-12-20 00:21:57
【问题描述】:

我正在查看所有以前的答案,但没有一个能够解决我的问题。 我是 react 新手,目前正在从事一个宠物项目,我正在使用 react 与 typescript、redux 和材料 UI。

出于某种原因,尝试从组件内部重定向会更改 url,但不会呈现
匹配的组件(即,尽管实际路径位于同一路由器下,但它默认为我的 404 页面)。

我的代码如下 -
在这个页面上,我试图将用户重定向到项目页面,以防他已经登录 - 这里 componentDidMount 上的 history.push “失败”。
Login.tsx 页面:

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 页面:
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 页面:

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 类:

import { createBrowserHistory } from "history";

export const history = createBrowserHistory();

谁能告诉我我错过了什么?

【问题讨论】:

  • 我已经读了5遍了,什么都看不到。不过,我确实有两个问题,1. 为什么要将 Switch 组件包装在 div 中? 2. 为什么你从 react-router 导入路由器,而不是从 react-router-dom 导入?
  • 嗨!关于div - 这是一个错误,至于从react-router导入路由器,实际导入来自react-router-dom,我看到一个SO线程显示从react-router导入并认为我犯了一个错误所以改变了它到 react-router,那么我该如何解决我的错误(甚至有错误)?
  • 因此,当您登录登录时,它会重定向到正确的 url,但会显示 NotFound 组件而不是项目。当您正常进行项目时,它会起作用吗?当你登出时去登入,它有效吗?不时重启服务器也有帮助:)
  • 我读过其他人有类似的问题。你有哪个 npm 版本的历史?这显然是我发现的history v 5.^ has a bug
  • @tachko 伙计谢谢你!!!!降级到历史 v4.10.1 解决了我的问题!!!你摇滚! (在过去的 4 天里,我真的被它困住了!!)

标签: reactjs typescript react-redux material-ui react-router-dom


【解决方案1】:

发布@tachko 评论作为答案,最新的历史版本有一个错误,降级到 V4.10.1 解决了我的问题(使用 V5.0.0)https://github.com/ReactTraining/react-router/issues/7415

【讨论】:

    猜你喜欢
    • 2019-03-09
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 2021-02-08
    • 1970-01-01
    • 2020-06-01
    • 2018-04-01
    • 2012-11-10
    相关资源
    最近更新 更多