iam using react-redux here and when i use trycatch block it not triggers error when error is executed.why??
import {
LOGIN_REQUEST,
LOGIN_SUCCESS,
LOGIN_FAIL,
CLEAR_ERRORS,
} from "../constants/userConstant";
impo
rt axios from "axios";
export const login = (email, password) => async (dispatch) => {
try {
dispatch({ type: LOGIN_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.post(
`/api/v1/login`,
{ email, password },
config
);
dispatch({ type: LOGIN_SUCCESS, payload: data.user });
} catch (error) {
dispatch({ type: LOGIN_FAIL, payload: error.response.data.message });
}
};
//Clearing errors
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
=>It was showing LOGIN_FAIL in react-redux-devtools but it was not throwing an error
catch (error) {
dispatch({ type: LOGIN_FAIL, payload: error.response.data.message });
// Log your error in the console
console.log(error);
// throw error
throw error
}
If you want to console error or throw error do it this way but you actually just call your dispatch for the moment.
The try catch blocks are here to handle every situations your function meet, when an error come it's been caught but YOU have to decide how to handle this error.
Related
I am using Next.js. I have created an Axios interceptor where a rejected Promise will be returned. But where there is a server-specific error that I need. Next.js is showing the error in the application like this.
And there is the code of the Axios interceptor and instance.
import axios from "axios";
import store from "../redux/store";
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
let token = "";
if (typeof window !== 'undefined') {
const item = localStorage.getItem('key')
token = item;
}
const axiosInstance = axios.create({
baseURL: publicRuntimeConfig.backendURL,
headers: {
Authorization: token ? `Bearer ${token}` : "",
},
});
axiosInstance.interceptors.request.use(
function (config) {
const { auth } = store.getState();
if (auth.token) {
config.headers.Authorization = `Bearer ${auth.token}`;
}
return config;
},
function (error) {
return Promise.reject(error);
}
);
axiosInstance.interceptors.response.use(
(res) => {
console.log(res)
return res;
},
(error) => {
console.log(error)
return Promise.reject(error);
}
);
export default axiosInstance;
Also, I am using redux and there is the action.
import axios from "../../api/axios";
import { authConstants } from "../types";
export const login = (data) => {
return async (dispatch) => {
try {
dispatch({
type: authConstants.LOGIN_REQUEST,
});
const res = axios.post("/user/login", data);
if (res.status === 200) {
dispatch({
type: authConstants.LOGIN_SUCCESS,
payload: res.data,
});
}
} catch (error) {
console.log(error, authConstants);
dispatch({
type: authConstants.LOGIN_FAILURE,
payload: { error: error.response?.data?.error },
});
}
};
};
Your problem is here...
const res = axios.post("/user/login", data);
You're missing await to wait for the response
const res = await axios.post("/user/login", data);
This fixes two things...
Your code now waits for the response and res.status on the next line will be defined
Any errors thrown by Axios (which surface as rejected promises) will trigger your catch block. Without the await this does not happen and any eventual promise failure bubbles up to the top-level Next.js error handler, resulting in the popup in your screenshot.
I am trying to print the error in my console but my console is not displaying anything. Console.log(err) is in the catch block right before dispatch(returnErrors(err.response.data, err.response.status, 'LOGIN_FAIL'));
//Login user
export const login = ({email, password, history}) => dispatch =>{
const config = {
headers:{
"Content-Type": "application/json"
}
};
const body = JSON.stringify({email, password});
axios.post('/api/user/login/', body, config)
.then(res => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
history.push('/userfeed')
})
.catch(err =>{
console.log(err)
dispatch(returnErrors(err.response.data, err.response.status, 'LOGIN_FAIL'));
dispatch({
type: LOGIN_FAIL
});
});
};
I implemented my own way to handle access/refresh token. Basically when accessToken is expired, it awaits the dispatch of another action and, if it is successful, it dispatch again itself. The code below explains it better:
export const refresh = () => async (dispatch) => {
dispatch({
type: REFRESH_USER_FETCHING,
});
try {
const user = await api.refresh();
dispatch({
type: REFRESH_USER_SUCCESS,
payload: user,
});
return history.push("/");
} catch (err) {
const { code } = err;
if (code !== "ACCESS_TOKEN_EXPIRED") {
dispatch({
type: REFRESH_USER_ERROR,
payload: err,
});
const pathsToRedirect = ["/signup"];
const {
location: { pathname },
} = history;
const path = pathsToRedirect.includes(pathname) ? pathname : "/login";
return history.push(path);
}
try {
await dispatch(refreshToken());
return dispatch(refresh());
} catch (subErr) {
dispatch({
type: REFRESH_USER_ERROR,
payload: err,
});
return history.push("/login");
}
}
};
export const refreshToken = () => async (dispatch) => {
dispatch({
type: REFRESH_TOKEN_FETCHING,
});
try {
await api.refreshToken();
dispatch({
type: REFRESH_TOKEN_SUCCESS,
});
} catch (err) {
dispatch({
type: REFRESH_TOKEN_ERROR,
payload: err,
});
}
};
the issue is that I am finding it really difficult to test with Jest. In fact, I have implemented this test:
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import * as actionCreators from "./actionCreators";
import * as actions from "./actions";
import api from "../../api";
jest.mock("../../api");
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe("authentication actionCreators", () => {
it("runs refresh, both token expired, should match the whole flow", async () => {
api.refresh.mockRejectedValue({
code: "ACCESS_TOKEN_EXPIRED",
message: "jwt expired",
});
api.refreshToken.mockRejectedValue({
code: "REFRESH_TOKEN_EXPIRED",
message: "jwt expired",
});
const expectedActions = [
{ type: actions.REFRESH_USER_FETCHING },
{ type: actions.REFRESH_TOKEN_FETCHING },
{ type: actions.REFRESH_TOKEN_ERROR },
{ type: actions.REFRESH_USER_ERROR },
];
const store = mockStore({ auth: {} });
await store.dispatch(actionCreators.refresh());
expect(store.getActions()).toEqual(expectedActions);
});
});
but instead of completing, the test runs indefenitely. This issue is not happening when I am testing it manually, so I think there is something missing in Jest, so my question is: is there a way to test this recursive behaviour?
Thanks
The problem is await you use with dispatch, dispatch returns an action, not a Promise, use Promise.resolve instead.
I use in my react application redux saga. There i have a login form. With redux saga i try to handle the error when user login.
Bellow is my saga:
function* postLoginUserReq(user) {
const {name} = user.values.user;
try {
const data = yield call(() => {
return fetch("url", {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: name,
}),
}).then(data => data.json()).then(response => {
userErrorLogIn(response.error) //here i check if appears an error
})
});
} catch (error) {
console.log(error);
}
}
Bellow is action creator:
export const userErrorLogIn = (error) => {
console.log(error) //the error message appears here
return {
type: USER_ERROR_LOGIN,
payload: error
};
};
Bellow is reducer:
case USER_ERROR_LOGIN: {
console.log(action.payload) //here the error message does not appears (why?)
return {
...state,
userIsLoggedError:action.payload,
}
}
Question: What could be the issue that i don't get the error in reducer?
You can use .catch for that -
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
fetch("http://httpstat.us/500")
.then(handleErrors)
.then(response => console.log("ok") )
.catch(error => userErrorLogIn(error) );
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
I have made a user login function:
export const userLogin = (email, password) => (dispatch) => {
console.log(email, password);
dispatch({ type: actionTypes.AUTH_LOGIN_STARTED });
console.log("after dispatch");
Parse.User.logIn(email, password, {
success(user) {
console.log("in success");
dispatch({
type: actionTypes.AUTH_LOGIN_SUCCESS,
user: user.toJSON(),
});
window.location.replace('/');
},
error(user, error) {
console.log("in error")
console.log({ error });
// The login failed. Check error to see why.
dispatch({
type: actionTypes.AUTH_LOGIN_ERROR,
error,
});
},
});
};
but it always gets stuck after Parse.User.logIn it doesn't go in a success or in error. I have logged the email and password and they are correct.
So what am I missing here?
Parse.User.Login does not have any third parameter. You should be using the promise functions:
Parse.User.logIn(email, password)
.then((user) => {
console.log("in success");
dispatch({
type: actionTypes.AUTH_LOGIN_SUCCESS,
user: user.toJSON(),
});
window.location.replace('/');
})
.error((user, error) => {
console.log("in error")
console.log({ error });
// The login failed. Check error to see why.
dispatch({
type: actionTypes.AUTH_LOGIN_ERROR,
error,
});
});
Or, if you're fancy enough, you can use the new await syntax (Which I think is a bit cleaner):
export const userLogin = (email, password) => async (dispatch) => {
console.log(email, password);
dispatch({ type: actionTypes.AUTH_LOGIN_STARTED });
console.log("after dispatch");
try {
const user = await Parse.User.logIn(email, password);
console.log("in success");
dispatch({
type: actionTypes.AUTH_LOGIN_SUCCESS,
user: user.toJSON(),
});
window.location.replace('/');
} catch (error) {
console.log("in error")
console.log({ error });
// The login failed. Check error to see why.
dispatch({
type: actionTypes.AUTH_LOGIN_ERROR,
error,
});
}
};