Reputation: 91
I want to show error messages like Incorrect password
as an alert or toast when the user enters the wrong password. Right now, I'm only seeing those messages in the network response tab and wondering how to display a response on the frontend.
Here's login controller function:
loginUser: async (req, res, next) => {
try {
console.log("inside login controller")
const { email, password } = req.body
if (!email || !password) {
return res.status(400).json({ message: "Email and password are must" })
}
if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invalid email" })
}
const user = await User.findOne({ email })
if (!user) {
return res.status(404).json({ message: "This email does not exist" })
}
if (!user.confirmPassword(password)) {
// console.log("Password in login controller", password)
return res.status(401).json({ message: "Incorrect password" })
}
res.status(200).json({ user })
} catch (error) {
return next(error)
}
}
And the login component looks like:
import React, { Component } from "react"
import validator from "validator"
import { loginUser } from "../actions/userActions"
import { connect } from "react-redux"
import { Link } from "react-router-dom"
import { toastError } from "../../utils/toastify"
class LoginForm extends Component {
constructor(props) {
super(props)
this.state = {
email: "",
password: "",
}
}
handleChange = (event) => {
const { name, value } = event.target
this.setState({
[name]: value,
})
}
handleSubmit = (event) => {
event.preventDefault()
const { email, password } = this.state
const loginData = {
email: this.state.email,
password: this.state.password,
}
if (!email || !password) {
return toastError("Email and password are must.")
}
if (password.length < 6) {
return toastError("Password must contain 6 characters.")
}
if (!validator.isEmail(email)) {
return toastError("Invalid email.")
}
this.props.dispatch(
loginUser(loginData, () => this.props.history.push("/"))
)
}
render() {
const isAuthInProgress = this.props.auth.isAuthInProgress
return (
<div>
<div className="field">
<p className="control has-icons-left has-icons-right">
<input
className="input"
onChange={this.handleChange}
name="email"
value={this.state.email}
type="email"
placeholder="Email"
/>
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left">
<input
className="input"
onChange={this.handleChange}
name="password"
value={this.state.password}
type="password"
placeholder="Password"
/>
<span className="icon is-small is-left">
<i className="fas fa-lock"></i>
</span>
</p>
</div>
<div className="field">
<p className="control">
{isAuthInProgress ? (
<button className="button is-success is-loading">Login</button>
) : (
<button onClick={this.handleSubmit} className="button is-success">
Login
</button>
)}
</p>
</div>
<Link to="/forgot-password">
<p className="has-text-danger">Forgot password?</p>
</Link>
</div>
)
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps)(LoginForm)
login action
export const loginUser = (loginData, redirect) => {
return async (dispatch) => {
dispatch({ type: "AUTH_STARTS" })
try {
const res = await axios.post(`${baseUrl}/users/login`, loginData)
console.log(res.data)
dispatch({
type: "AUTH_SUCCESS",
data: { user: res.data.user }
})
localStorage.setItem("authToken", res.data.token)
redirect()
toastSuccess("You are now logged in!")
} catch (err) {
console.log
dispatch({
type: "AUTH_ERROR",
data: { err },
})
}
}
}
auth reducer
const auth = (state = initialState, action) => {
switch (action.type) {
case "AUTH_STARTS":
return {
...state,
isAuthInProgress: true,
isAuthenticated: false,
authError: null,
}
case "AUTH_SUCCESS":
return {
...state,
isAuthInProgress: false,
authError: null,
isAuthenticated: true,
isIdentifyingToken: false,
user: action.data.user,
}
case "AUTH_ERROR":
return {
...state,
isAuthInProgress: false,
authError: action.data.error,
isAuthenticated: false,
user: {},
}
Also, is there a need for similar checks like I have done both in controller and the component like if (!email || !password)
and if (!validator.isEmail(email))
. Or should it be only in the backend?
Upvotes: 1
Views: 1819
Reputation: 2186
Since you already have a separate state in the reducer in place authError
which holds the API error response, you can leverage it by using a callback function on your loginUser
dispatch.
handleSubmit = (event) => {
/* your-validation-code */
this.props.dispatch(loginUser(loginData, this.checkForLoginError()));
};
checkForLoginError = () => {
const authenticationError = this.props.auth.authError;
if (authenticationError === null) {
this.props.history.push("/");
} else {
return toastError(authenticationError);
}
};
Upvotes: 1