Я получаю TypeError для свойства "токен" после отправки формы для регистрации учетной записи (аутентификация JWT).Я также использую React-Redux.
Кроме того, есть ли другой способ, кроме хранения в localStorage?
Ниже приведены коды для редуктора аутентификации и действия.
authReducer.js
import {
USER_LOADING,
USER_LOADED,
AUTH_ERROR,
REGISTER_SUCCESS,
REGISTER_FAIL
} from '../actions/types';
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
isLoading: false,
user: null
};
export default function (state = initialState, action) {
switch (action.type) {
case USER_LOADING:
return {
...state,
isLoading: true
};
case USER_LOADED:
return {
...state,
isAuthenticated: true,
isLoading: false,
user: action.payload
};
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
isLoading: false
};
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT_SUCCESS:
case REGISTER_FAIL:
localStorage.removeItem('token');
return {
...state,
token: null,
user: null,
isAuthenticated: false,
isLoading: false
};
default:
return state;
}
}
auth.Actions
import axios from 'axios';
import { returnErrors } from './errorActions';
import {
USER_LOADING,
USER_LOADED,
AUTH_ERROR,
REGISTER_SUCCESS,
REGISTER_FAIL
} from './types';
//check token and load user
export const loadUser = () => (dispatch, getState) => {
//User loading
dispatch({ type: USER_LOADING });
axios
.get('/api/auth/user', tokenConfig(getState))
.then(res =>
dispatch({
type: USER_LOADED,
payload: res.data
})
)
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status));
dispatch({
type: AUTH_ERROR
});
});
};
//register user
export const register = ({ name, email, password }) => dispatch => {
//headers
const config = {
headers: {
'Content-Type': 'application/json'
}
};
//request body
const body = JSON.stringify({ name, email, password });
axios
.post('api/users', body, config)
.then(res =>
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
})
)
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status, 'REGISTER_FAIL'));
dispatch({
type: REGISTER_FAIL
});
});
};
//setup config/headers and token
export const tokenConfig = getState => {
//get token from localstorage
const token = getState().auth.token;
//headers
const config = {
headers: {
'Content-Type': 'application/json'
}
};
//if token, add to headers
if (token) {
config.headers['x-auth-token'] = token;
}
return config;
};
users.js
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const config = require('config');
const jwt = require('jsonwebtoken');
//User Model
const User = require('../../models/User');
//@route POST api/users
router.post('/', (req, res) => {
const { name, email, password } = req.body;
//validation
if (!name || !email || !password) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
//check for existing users
User.findOne({ email }).then(user => {
if (user) {
return res.status(400).json({ msg: 'User already exists!' });
}
const newUser = new User({
name,
email,
password
});
//create salt & hash
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save().then(user => {
jwt.sign(
{ id: user.id },
config.get('jwtSecret'),
{
expiresIn: 3600
},
(err, token) => {
if (err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
}
);
});
});
});
});
});
module.exports = router;
signup.js (компонент)
import React, { useState } from "react";
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 Link from "@material-ui/core/Link";
import Grid from "@material-ui/core/Grid";
import Box from "@material-ui/core/Box";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Logo from "./assets/images/logo_transparent.png";
//import Register from "./components/Register";
//redux
import { connect } from "react-redux";
//proptypes
import PropTypes from "prop-types";
import { register } from "./actions/authActions";
const useStyles = makeStyles(theme => ({
"@global": {
body: {
backgroundColor: theme.palette.common.white
}
},
paper: {
marginTop: theme.spacing(8),
display: "flex",
flexDirection: "column",
alignItems: "center"
},
form: {
width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(3)
},
submit: {
margin: theme.spacing(3, 0, 2),
background: "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)",
border: 0,
borderRadius: 3,
boxShadow: "0 3px 5px 2px rgba(255, 105, 135, .3)",
color: "white"
},
home: {
margin: theme.spacing(0, 0, 2),
border: 0,
borderRadius: 3,
boxShadow: "0 3px 5px 2px rgba(255, 105, 135, .3)"
}
}));
function SignUp(props) {
const classes = useStyles();
const [form, setValues] = useState({
name: "",
email: "",
password: "",
msg: null
})
;
const onChange = e => {
setValues({
...form,
[e.target.name]: e.target.value,
[e.target.email]: e.target.value,
[e.target.password]: e.target.value
});
};
const handleClick = e => {
e.preventDefault();
const { name, email, password } = form;
//create user object
const newUser = {
name,
email,
password
};
//attempt to register
props.register(newUser);
alert("Registration details submitted (test)" + name + email +
password);
};
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar
alt="logo"
src={Logo}
style={{
width: 150,
height: 150
}}
/>
<form className={classes.form} noValidate>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
autoComplete="name"
name="name"
variant="outlined"
required
fullWidth
id="name"
label="Full Name"
autoFocus
onChange={onChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
onChange={onChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
name="password"
label="Password"
type="password"
autoComplete="current-password"
onChange={onChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
name="password"
label="Confirm Password"
type="password"
id="password"
autoComplete="current-password"
/>
</Grid>
<Grid item xs={12} />
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={handleClick}
>
Sign Up
</Button>
<Button
href="http://localhost:3000/"
fullWidth
variant="contained"
color="primary"
className={classes.home}
>
Home
</Button>
<br />
<br />
<Grid container justify="flex-end">
<Grid item>
<Link href="http://localhost:3000/signin" variant="body2">
Already have an account? Sign in
</Link>
</Grid>
</Grid>
</form>
</div>
<Box mt={5}>
<MadeWithLove />
</Box>
</Container>
);
}
SignUp.propTypes = {
isAuthenticated: PropTypes.bool,
error: PropTypes.object.isRequired,
register: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated,
error: state.error //getting from reducer
});
export default connect(
mapStateToProps,
{ register } //from redux actions //mapdispatchtoprop
)(SignUp); //component
// скриншот ошибки