Запрос Axios не работает с React + Redux - PullRequest
1 голос
/ 05 июня 2019

Я попытаюсь дать предпосылку, а затем дополню ее кодом.

Я решил внедрить UI Material в свой проект React, и я получил большую часть работы.Как настроено приложение, пользователь сталкивается со страницей входа.Модуль Login.js отображает модуль SignIn.js , вводит их учетные данные и нажимает кнопку "Отправить".formData, onChange и onSubmit передаются в качестве реквизитов в компонент SignIn из компонента Login - и компонент Login получает их через mapStateToProps.Компонент Login использует промежуточное программное обеспечение connect для связывания избыточного состояния с приложением реагирования.

Нажатие на кнопку submit запускает formData (в компоненте Login, который передается в компонент SignIn) для попадания в мой метод login, расположенный в "../../actions/auth";.Ошибка происходит из-за этого метода, при вызове axios в try catch, где я пытаюсь связаться с бэкэндом const response = await axios.post("/api/auth", body, config);

Что странно, что dispatch({ type: LOGIN_SUCCESS, payload: response.data }); никогда не получает удар, который должен быть установленсостояние токена возвращается из бэкэнда, так как кажется, что LOGIN_SUCCESS никогда не выполняется.Но что ОЧЕНЬ странно, так это то, что консоль, регистрирующая токен, на самом деле работает!Кажется, что он никогда не сохраняется, заставляя вызываться AUTH_ERROR.

Это мой компонент Login:

 // 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 получит возможность отправить его обратно.

1 Ответ

0 голосов
/ 05 июня 2019

Не знаю, как это объяснить, но я решил это, избавившись от триггера onSubmit из тега формы и поместив его в файл SignIn.js. Я изменил тип кнопки, чтобы тип кнопки, а также. Все заработало:)

...