【问题标题】:Axios request not working with React + ReduxAxios 请求不适用于 React + Redux
【发布时间】:2019-10-20 10:59:09
【问题描述】:

我会尝试给出一个前提,然后用代码跟进它。

我决定在我的 React 项目中实现 Material UI,并且我完成了大部分工作。应用程序是如何设置的,用户将面临登录页面。 Login.js 模块呈现SignIn.js module,输入他们的凭据,然后单击提交。 formDataonChangeonSubmit 作为 props 从 Login 组件传递给 SignIn 组件 - Login 组件通过 mapStateToProps 接收这些。 Login 组件使用 connect 中间件将 redux 状态链接到 react 应用程序。

单击提交会触发 formData(在传递给 SignIn 组件的 Login 组件中)点击位于 "../../actions/auth"; 中的我的 login 方法。错误发生在此方法内部,在 try catch 中的 axios 调用中,我尝试与后端通信const response = await axios.post("/api/auth", body, config);

奇怪的是dispatch({ type: LOGIN_SUCCESS, payload: response.data }); 从未被命中,这应该将状态设置为从后端返回的令牌,因为似乎LOGIN_SUCCESS 从未被执行。但非常奇怪的是,控制台记录令牌确实有效!似乎它从未被存储,而是强制 AUTH_ERROR 被调用。

这是我的登录组件:

 // Login.js
 import SignIn from "../../material/SignIn";

 const Login = ({ setAlert, login, isAuthenticated }) => {
 const [formData, setFormData] = useState({
 email: "",
 password: ""
 });

 const { email, password } = formData;

 const onChange = e => {
 setFormData({ ...formData, [e.target.name]: e.target.value });
 };

 const onSubmit = e => {
 login(email, password);
 };
 // Redirect if logged in
 if (isAuthenticated) {
 return <Redirect to="/dashboard" />;
 }

 return (
 <Fragment>
      <SignIn
      email={email}
      password={password}
      onSubmit={onSubmit}
      onChange={onChange}
      isAuthenticated={isAuthenticated}
      />
 </Fragment>
 );
 };

 Login.propTypes = {
 setAlert: PropTypes.func.isRequired,
 login: PropTypes.func.isRequired,
 isAuthenticated: PropTypes.bool
 };

 const mapStateToProps = state => ({
 isAuthenticated: state.auth.isAuthenticated
 });

 export default connect(
 mapStateToProps,
 { setAlert, login }
 )(Login);

它正在渲染的 SignIn 组件在这里:

 // SignIn.js
 export default function SignIn({ email, password, onChange, onSubmit }) {
 const classes = useStyles();

 return (
 <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
      <Avatar className={classes.avatar}>
           <LockOutlinedIcon />
      </Avatar>
      <Typography component="h1" variant="h5">
           Sign in
      </Typography>
      <form onSubmit={e => onSubmit(e)} className={classes.form} noValidate>
           <TextField
           variant="outlined"
           margin="normal"
           required
           onChange={e => onChange(e)}
           fullWidth
           id="email"
           label="Email Address"
           name="email"
           value={email}
           // autoComplete="email"
           autoFocus
           />
           <TextField
           variant="outlined"
           margin="normal"
           required
           onChange={e => onChange(e)}
           fullWidth
           name="password"
           label="Password"
           type="password"
           value={password}
           id="password"
           autoComplete="current-password"
           />
           <FormControlLabel
           control={<Checkbox value="remember" color="primary" />}
           label="Remember me"
           />
           <Button
           type="submit"
           fullWidth
           variant="contained"
           color="primary"
           className={classes.submit}
           >
           Sign In
           </Button>
           <Grid container>
           <Grid item xs>
           <Link href="#" variant="body2">
                Forgot password?
           </Link>
           </Grid>
           <Grid item>
           <Link href="#" variant="body2">
                {"Don't have an account? Sign Up"}
           </Link>
           </Grid>
           </Grid>
      </form>
      </div>
      <Box mt={5}>
      <MadeWithLove />
      </Box>
 </Container>
 );
 }

单击提交按钮会在我的登录组件中引发onSubmit 方法:

 // Login user
 export const login = (email, password) => async dispatch => {
 // Config needed because we're sending data
 const config = {
 headers: {
      "Content-Type": "application/json"
 }
 };

 const body = JSON.stringify({ email, password });

 try {
 const response = await axios.post("/api/auth", body, config);

 // Skips over this dispatch
 dispatch({
      type: LOGIN_SUCCESS,
      payload: response.data
 });

 // But hits this dispatch.. and then console logs 'REACHED' as seen below
 dispatch(loadUser());
 } catch (err) {
 const errors = err.response.data.errors;

 if (errors) {
      errors.forEach(error => {
      dispatch(setAlert(error.msg, "danger"));
      });
 }

 dispatch({
      type: LOGIN_FAIL
 });
 }
 };

如果你注意到,在 axios 调用之后,loadUser is called,定义为:

 // Load user
 export const loadUser = () => async dispatch => {
 const token = localStorage.token;

 console.log('REACHED!'); // reached

 if (token) {
 setAuthToken(token);
 }

 try {
 const response = await axios.get("/api/auth");

 dispatch({
      type: USER_LOADED,
      payload: response.data
 });
 } catch (err) {
 dispatch({
      type: AUTH_ERROR // This is dispatched
 });
 }
 };

后端路由如下:

 // @route  POST api/auth
 // @desc   Authenticate user and get token
 // @access Public
 router.post(
 "/",
 [
 check("email", "Please include a valid email").isEmail(),
 check("password", "Please is required").exists()
 ],
 async (req, res) => {
 const errors = validationResult(req);

 // send back any errors
 if (!errors.isEmpty()) {
      return res.status(400).json({
      errors: errors.array()
      });
 }

 const { email, password } = req.body;

 try {
      // check if user exists, send error if so
      let user = await User.findOne({ email });

      if (!user) {
      return res
           .status(400)
           .json({ errors: [{ msg: "Invalid credentials" }] });
      }

      const isMatch = await bcrypt.compare(password, user.password);

      if (!isMatch) {
      return res
           .status(400)
           .json({ errors: [{ msg: "Invalid credentials" }] });
      }
      // return jsonwebtoken so that way they're logged in right
      // away, without having to log in after registering
      const payload = {
      user: {
           id: user.id
      }
      };

      jwt.sign(
      payload,
      config.get("jwtSecret"),
      {
           expiresIn: process.env.PORT ? 3600 : 36000
      },
      (err, token) => {
           if (err) throw err;

           console.log(token); // prints token!

           return res.json({ token });
      }
      );
 } catch (err) {
      console.log(err);
      res.status(500).send("Server error");
 }
 }
 );

在这一点上我很困惑。令牌正在呈现,但似乎 React 并没有在 Node 有机会将其发回之前“等待”响应。

【问题讨论】:

  • 你用的是什么中间件?它需要支持异步操作。
  • 您说“发生错误...”但不是很清楚在哪里以及发生了什么错误。是 AJAX 请求中的错误吗?还是 JS 异常? “记录令牌确实有效”您在哪里尝试记录令牌?
  • @EricHasselbring 在我决定尝试实现 Material UI 之前,一切正常。
  • @ThiagoBarcala 在我在问题中发布的最后一个函数中,在 JWT.sign 中的 POST api/auth 中。似乎令牌是在后端创建的,但没有在前端的const response = await.. 中返回

标签: node.js reactjs redux react-redux


【解决方案1】:

不知道如何解释,但我通过从表单标签中删除 onSubmit 触发器并将其放在 SignIn.js 中来解决它。我也将按钮的类型更改为键入按钮。搞定了:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 2020-06-15
    • 2020-07-28
    相关资源
    最近更新 更多