Returning response from Dispatch - reactjs

I'm implementing login functionality to my application and am trying to return the response from a dispatched thunk action. I'm purposely entering the incorrect password because i'd like for the errors that are set in my backend to display within an antd notification on the frontend. My expected response should be:
return res.status(400).json({
success: false,
message: 'Invalid email address or password.',
});
but instead i'm getting the following in the console:
Error: Request failed with status code 400
Route:
const loginUser = async (req, res) => {
// Validate Login Input
const { error } = validateLoginInput(req.body);
if (error)
return res
.status(400)
.json({ success: false, message: error.details[0].message });
req.body.email = req.body.email.toLowerCase();
req.body = sanitize(req.body);
const { email, password } = req.body;
try {
// See if user exists
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({
success: false,
message: 'Invalid email address or password.',
});
}
// Compare passwords
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({
success: false,
message: 'Invalid email address or password.',
});
}
// Return jsonwebtoken
const payload = {
user: {
id: user.id,
},
};
jwt.sign(
payload,
process.env.JWT_SECRET,
{ expiresIn: 3600 },
(error, token) => {
if (error) throw error;
res.json({ token });
}
);
} catch (error) {
res.status(500).json({ success: false, messsage: 'Server error' });
}
};
Actions:
export const loginBegin = () => ({
type: LOGIN_BEGIN,
});
export const loginSuccess = (user) => ({
type: LOGIN_SUCCESS,
payload: user,
});
export const loginFail = (error) => ({
type: LOGIN_FAIL,
payload: error,
});
Thunk:
export const attemptLogin = (formData) => async (dispatch) => {
dispatch(loginBegin());
return await postLogin(formData)
.then((res) => {
dispatch(loginSuccess(res.data));
console.log(res.data); <-- This doesn't even show in console
})
// .then(() => {
// setTimeout(() => {
// dispatch(push('/app'));
// }, 2000);
// })
.catch((error) => {
dispatch(loginFail(error));
});
};
onSubmit:
const onSubmit = async (values) => {
const { email, password } = values;
setLoading(true);
try {
const response = await dispatch(attemptLogin({ email, password }));
console.log(response); <-- returns undefined
if (response.data.success) {
setLoading(false);
notification['success']({
message: 'Congrats!',
description: response.message,
});
} else {
notification['error']({
message: 'Uh-oh!',
description: response.message,
});
}
} catch (error) {
console.log(error);
}
};

The problem is that you are not returning the response in the promise. Also, if you are getting a 400 response, it probably rejects the promise or throws an error.
export const attemptLogin = (formData) => async (dispatch) => {
dispatch(loginBegin());
return await postLogin(formData)
.then((res) => {
dispatch(loginSuccess(res.data));
return res;
})
.catch((error) => {
dispatch(loginFail(error));
return error; // this will only work if the error is the response object.
});
};

Related

About firebase token service

i am following Ryan Dunghel Ecommerce course, i am using firebase admin to verify the token client sent, but some magic happened, the token ok for the first time i log, but after i refresh the page, everything gone, also happened with Google login,enter image description here i find out the similar question too, someone please give me advise, i dont want to stop thing course. thank.
App.js
const App = () => {
const dispatch = useDispatch();
// to check firebase auth state
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (user) {
const idTokenResult = await user.getIdTokenResult();
console.log("user", user);
currentUser(idTokenResult.token)
.then((res) => {
dispatch({`enter code here`
type: "LOGGED_IN_USER",
payload: {
name: res.data.name,
email: res.data.email,
token: idTokenResult.token,
role: res.data.role,
_id: res.data._id,
},
});
})
.catch((err) => console.log(err));
}
});
// cleanup
return () => unsubscribe();
}, [dispatch]);
login.js
const handleSubmit = async (e) => {
e.preventDefault();
// setLoading(true);
try {
let result = await signInWithEmailAndPassword(auth,email, password);
const { user } = result;
const TokenResult = await user.getIdTokenResult();
createOrUpdateUser(TokenResult.token)
.then((res) => {
dispatch({
type: userActionType.USER_LOGGED_IN,
payload: {
name: res.data.name,
email: res.data.email,
token: TokenResult.token,
role: res.data.role,
_id: res.data._id,
},
});
console.log(res);
roleRedirect(res);
})
.catch((err) => console.log(err));
} catch (error) {
console.log(error);
toast.error(error.message);
}
};
middleware, where verify token
exports.authCheck = async (req, res, next) => {
// console.log(req.headers); // token
try {
const firebaseUser = await admin
.auth()
.verifyIdToken(req.headers.authtoken);
// console.log("FIREBASE USER IN AUTHCHECK", firebaseUser);
req.user = firebaseUser;
next();
} catch (err) {
res.status(401).json({
err: "Invalid or expired token",
});
}
};

Get user info with JWT in React

I´m storing the user token in the localstorage after a user logs in, and i would like to know how i can use this token that is in the localstorage to fetch the user info.
backend login.js
module.exports.login = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email: email });
if (!user) return res.status(404).json({ message: "Usuário não cadastrado!" });
const isMatch = bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: "Email e/ou senha incorretos." });
const token = tokenGenereator(user._id);
return res.status(200).json({ result: user, token });
} catch (error) {
console.log(error)
}
};
frontend login.js
export const login = (data, history) => {
return async (dispatch) => {
try {
const userData = await api.login(data);
localStorage.setItem("userToken", JSON.stringify(userData.data.token));
dispatch(
setUser({
fullname: userData.data.fullname,
email: userData.data.email,
isAuth: true,
})
);
history.push("/home");
} catch (error) {
if (error.response && error.response.data.message) {
dispatch(
setNotification({
variant: "danger",
message: error.response.data.message,
})
);
}
}
};
};
I pass the token to the backend
// i would pass the jwt to the function as a parameter
export const fetchUser = (data) => {
return async (dispatch) => {
const user = await api.getUserData(data);
console.log(user);
};
};
And i recieve
module.exports.getCurrentUser = async (req, res) => {
const { token } = req.body;
// i dont know what to do from here...
};

How to send error messages from express to react/redux

I have a MERN app using redux. My actions look like this:
export const logIn = (logInData) => async (dispatch) => {
try {
const { data } = await api.logIn(logInData);
localStorage.setItem('auth', JSON.stringify(data))
dispatch({
type: LOG_IN,
payload: data
});
} catch (error) {
dispatch({
type: ADD_FLASH_MESSAGE,
payload: error
})
}
}
And my server looks like this
export const logIn = async (req, res) => {
const logInParams = req.body;
const user = await User.findOne({ email: logInParams.email });
if (!user) {
console.log("USER NOT FOUND");
res.status(400).json({
message: "Invalid credentials."
});
}
const passwordMatches = await bcrypt.compare(logInParams.password, user.password);
if (!passwordMatches) {
console.log("WRONG PASSWORD")
return res.status(400).json({
message: "Invalid credentials."
})
}
// Sign in user with jwt
const payload = {
user: {
id: user.id
}
}
jwt.sign(payload, config.get('jwtSecret'), (error, token) => {
if (error) throw error;
console.log('Successfully logged in');
return res.status(200).json({
token: token,
user: user,
loggedIn: true
});
})
}
I'm not able to access my error messages in my actions. I just get error messages like so
POST http://localhost:5000/auth/login 400 (Bad Request)
And my console.log looks like this:
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
How do I access my custom error messages from my server?
try to get the status as error.status and custom message as error.data.message
your custom error message contain in error.response.data
try change this in your action:
export const logIn = (logInData) => async (dispatch) => {
try {
const { data } = await api.logIn(logInData);
localStorage.setItem('auth', JSON.stringify(data))
dispatch({
type: LOG_IN,
payload: data
});
}
catch (error) {
dispatch({
type: ADD_FLASH_MESSAGE,
payload: **error.response.data**
})
}
}

Cannot read property of "undefined": POST request from React to Express

I have this controller to authenticate a user by their email and password
const authUser = asyncHandler(async (req, res, next) => {
console.log('Hit');
const { email, password } = req.body;
await User.findOne({ email }, async (err1, foundUser) => {
if (err1) {
next(err1);
} else if (foundUser && (await foundUser.matchPasswords(password))) {
res.json({
_id: foundUser._id,
name: foundUser.name,
email: foundUser.email,
token: generateWebToken(foundUser._id),
});
} else {
res.status(401);
next(new Error('Invalid credentials'));
}
});
});
where the mongoose instance method matchPasswords is defined as
userSchema.methods.matchPasswords = async function (enteredPassword) {
return await bcrypt.compare(
enteredPassword,
this.password,
(err, success) => {
console.log(success);
return success;
}
);
};
In the React frontend I have created an action creator loginUser like so
export const loginUser = ({ email, password }) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST });
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const { data } = await axios.post(
'/api/v1/users/login',
{ email, password },
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.message.data
: error.message,
});
}
};
However, when I try to login with the wrong credentials or the correct ones I receive this message in the screen and console Uncaught (in promise) TypeError: Cannot read property 'data' of undefined. Above that there is this error main.js?attr=bsXXNSP9r2dL_fbuBOkoev2GjgusyPgY7MC7K-twziLtf_MItBzQdXJ4l_HgsPQw:2699 POST http://localhost:3000/api/v1/users/login 401 (Unauthorized) which made me think maybe it's proxy error because my server is running on port 5000, but the console.log('Hit'); inside the authUser controller is fired when I make the request, which means the request reaches the backend, but there is some uncaught error. Where is my mistake?

react & redux with hooks: Actions must be plain objects. Use custom middleware for async actions

i tried looking for similar answers to help solve my problem but i couldn't find anything using react redux hooks. This code was from a tutorial and originally written using the Context api. I wanted to trying using it with react-redux-hooks, but i got stuck. Basically i'm trying to register a user with a name, email and password, then pass these three as an object to the express server which will validated it and give me back a jwt token. Then come back to the client side and send the token to the reducer, which adds the token to localstorage and sets the state to isAuthenticated. The error i get is on the dispatch.
Dispatch
const onSubmit = e => {
e.preventDefault();
if (name === "" || email === "" || password === "") {
dispatch(setAlert("Please enter all fields", "danger"));
} else if (password !== password2) {
dispatch(setAlert("Passwords do not match", "danger"));
} else {
dispatch(register({ name, email, password })); // Error is here
}
setTimeout(() => {
dispatch(removeAlert());
}, 5000);
};
Action
export const register = async formData => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
const res = await axios.post("/api/users", formData, config);
return {
type: "REGISTER_SUCCESS",
payload: res.data
};
} catch (err) {
return {
type: "REGISTER_FAIL",
payload: err.response.data.msg
};
}
};
Reducer
const authReducer = (
state = {
token: localStorage.getItem("token"),
isAuthenticated: null,
loading: true,
user: null,
error: null
},
action
) => {
switch (action.type) {
case "REGISTER_SUCCESS":
console.log("register success");
localStorage.setItem("token", action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
loading: false
};
case "REGISTER_FAIL":
console.log("register failed");
localStorage.removeItem("token");
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
error: action.payload
};
default:
return state;
}
};
Store
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|| compose;
const store = createStore(
allReducers,
composeEnhancers(applyMiddleware(thunk))
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Express server
router.post(
"/",
[
check("name", "Please a name")
.not()
.isEmpty(),
check("email", "Please include a valid email").isEmail(),
check(
"password",
"Please enter a password with 6 or more characters"
).isLength({
min: 6
})
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const { name, email, password } = req.body;
try {
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({
msg: "User already exists"
});
}
user = new User({
name,
email,
password
});
// hash passsword
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get("jwtSecret"),
{
expiresIn: 360000
},
(err, token) => {
if (err) throw err;
res.json({
token
});
}
);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
I believe this question has answers to the issue your experiencing here: how to async/await redux-thunk actions?
Using this example, it may look something like this (wasn't able to test it):
export const register = formData => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
const request = axios.post("/api/users", formData, config);
return dispatch => {
const onSuccess = success => {
dispatch({
type: "REGISTER_SUCCESS",
payload: success.data
});
return success;
};
const onError = error => {
dispatch({
type: "REGISTER_FAIL",
payload: error.response.data.msg
});
return error;
};
request.then(onSuccess, onError);
};
};
export const register = formData => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
return async dispatch => {
const onSuccess = success => {
dispatch({
type: "REGISTER_SUCCESS",
payload: success.data
});
return success;
};
const onError = error => {
dispatch({
type: "REGISTER_FAIL",
payload: error.response.data.msg
});
return error;
};
try {
const success = await axios.post("/api/users", formData, config);
return onSuccess(success);
} catch (error) {
return onError(error);
}
}
};

Resources