Я попытаюсь дать предпосылку, а затем дополню ее кодом.
Я решил внедрить 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 получит возможность отправить его обратно.