I am developing a (simple-to-use) note-taking web application on my localhost (port 3000 for frontend, port 5000 for the backend). I'm having trouble implementing the user login functionality. I'll try to distill the problem here.
My main App.jsx component has a LoginScreen.jsx component as a child, which is here:
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import AuthService from '../services/auth.js';
import './LoginScreen.css';
const LoginScreen = ({ history }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
useEffect(() => {
if (localStorage.getItem('authToken')) history.push('/');
}, [history]);
const loginHandler = async e => {
e.preventDefault();
try {
const data = { email, password };
AuthService.loginUser(data).then(response => {
localStorage.setItem('authToken', response.data.token);
});
history.push('/');
} catch (error) {
setError(error.response.data.error);
setTimeout(() => {
setError('');
}, 5000);
}
};
return (
<div className="login-screen">
<form
className="login-screen__form"
onSubmit={loginHandler}
autoComplete="off"
>
<h3 className="login-screen__title">Login</h3>
{error && <span className="error-message">{error}</span>}
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
type="email"
required
id="email"
placeholder="enter email"
value={email}
onChange={e => setEmail(e.target.value)}
tabIndex={1}
/>
</div>
<div className="form-group">
<label htmlFor="password">
Password:{' '}
<Link
to="/forgotpassword"
className="login-screen__forgotpassword"
tabIndex={4}
>
Forgot Password?
</Link>
</label>
<input
type="password"
required
id="password"
placeholder="enter password"
value={password}
onChange={e => setPassword(e.target.value)}
tabIndex={2}
/>
</div>
<button type="submit" className="btn btn-primary" tabIndex={3}>
Login
</button>
<span className="login-screen__subtext">
Don't have an account? <Link to="/register">Register</Link>
</span>
</form>
</div>
);
};
export default LoginScreen;
When the user clicks "Login," that should trigger the AuthService, shown here:
import http from '../routing/http-auth.js';
class AuthService {
loginUser = data => http.post('/login', data);
registerUser = data => http.post('/register', data);
userForgotPassword = data => http.post('/forgotpassword', data);
userPasswordReset = data => http.put('/passwordreset/:resetToken', data);
}
export default new AuthService();
The http in the above file is an axios instance, shown below.
import axios from 'axios';
export default axios.create({
baseURL: 'http://localhost:5000/api/v1/auth',
headers: {
'Content-type': 'application/json'
}
});
So, the request is routed to the backend, where it goes here:
import express from 'express';
import AuthController from './auth.controller.js';
const router = express.Router();
router.route('/register').post(AuthController.register);
router.route('/login').post(AuthController.login);
router.route('/forgotpassword').post(AuthController.forgotPassword);
router.route('/passwordreset/:resetToken').put(AuthController.passwordReset);
export default router;
And then here:
static login = async (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
if (!email || !password) {
return next(
new ErrorResponse('please provide an email and password', 400)
);
}
try {
const user = await User.findOne({ email }).select('+password');
if (!user) return next(new ErrorResponse('invalid credentials', 401));
const isMatch = await user.matchPasswords(password);
if (!isMatch) return next(new ErrorResponse('invalid credentials', 401));
sendToken(user, 200, res);
} catch (error) {
next(error);
}
};
This all worked on a dummy application, but now, it's not working in my note-taking app. I get the error:
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
at loginHandler (LoginScreen.jsx:27)
How can I make this work?