Promise.all() keeps running even after error is thrown - javascript

in my React component, I have this:
componentDidMount() {
const {
dispatchGetCompanies,
dispatchGetDocumentTopics,
dispatchGetDataForCompanyTopicList,
dispatchGetDocumentTypes,
dispatchRequestComplete,
dispatchRequestInProgress,
dispatchRequestError,
} = this.props;
dispatchRequestInProgress()
Promise.all([
dispatchGetCompanies(),
dispatchGetDocumentTopics(),
dispatchGetDocumentTypes(),
dispatchGetDataForCompanyTopicList(),
]).then(() => {
dispatchRequestComplete();
}).catch((error) => {
dispatchRequestError(error);
});
}
const mapDispatchToProps = function (dispatch) {
return {
dispatchGetCompanies: (...args) => dispatch(getCompanies(...args)),
dispatchGetDocumentTopics: (...args) => dispatch(getDocumentTopics(...args)),
dispatchGetDataForCompanyTopicList: (...args) => dispatch(getDataForCompanyTopicList(...args)),
dispatchDeleteCompanyTopic: (...args) => dispatch(deleteCompanyTopic(...args)),
dispatchGetDocumentTypes: (...args) => dispatch(getDocumentTypes(...args)),
dispatchRequestInProgress: (...args) => dispatch(requestInProgress(...args)),
dispatchRequestComplete: (...args) => dispatch(requestComplete(...args)),
dispatchRequestError: (...args) => dispatch(requestError(...args)),
};
};
The action for getDocumentTypes is this:
export function getDocumentTypes() {
return function (dispatch, getState, api) {
return api.get('/document-type-metadata')
.then(({data}) => dispatch(getDocumentTypesSuccess(data)));
};
}
export function getDocumentTypesSuccess(data) {
return {
type: GET_DOCUMENT_TYPES,
payload: data,
};
}
Reducer code is this:
function setDocumentTypes(state, action) {
const {
documentTypeMetadata
} = action.payload;
return state.merge({
documentTypes: List(documentTypeMetadata),
_metadata: Map({
isFetching: false,
}),
});
}
export default function (state = Map({
id: null,
companyTopics: List(),
companies: List(),
topics: List(),
documentTypes: List(),
_metadata: Map({
isFetching: false,
error: false,
}),
}), action) {
switch (action.type) {
case SECTION_IDENTIFICATION_REQUEST_COMPLETE:
return setRequestComplete(state, action);
case SECTION_IDENTIFICATION_REQUEST_IN_PROGRESS:
return setInProgress(state, action);
case SECTION_IDENTIFICATION_REQUEST_ERROR:
return setError(state, action);
case GET_COMPANIES:
return setCompanies(state, action);
case GET_DOCUMENT_TOPICS:
return setTopics(state, action);
case GET_COMPANY_TOPIC_LIST_DATA:
return setCompanyTopicListData(state, action);
case GET_DOCUMENT_TYPES:
return setDocumentTypes(state, action);
default:
return state;
}
}
The dispatchRequestError is similar; it sets the "error" object in the Redux store to whatever error is returned.
The problem I am having is that if the api call to getDocumentTypes returns back an error, I would expect the dispatchRequestError to immediately execute, with all the other promises (dispatchGetDocumentTopics, etc) being rejected. However, after the error is set, the other promises inside the Promise.all() still resolve, causing the error property to become undefined, which causes the error message that I'm showing on the page to appear, then quickly disappear.
What is going wrong?
Thank you

Related

Redux Toolkit: createAsyncThunk action dispatched and return rejected promise the action returns empty payload

There is action created using createAsyncThunk and this method return a promise which is handled like this
export const action = createAsyncThunk(
'/fullfilment/update-content-preference',
async (data, { getState }, thunkAPI) => {
try {
const state = getState();
const authToken = state.auth.token;
const { selectedShipmentId: shipmentId } = state.fullfilment;
const obj = {
...data,
shipmentId
};
const response = await axios.post(
'/fullfilment/update-content-preference',
obj,
{ headers: { Authorization: `Bearer ${authToken}` } }
);
return response;
} catch (err) {
if (err.response) {
return thunkAPI.rejectWithValue({
err: err.response,
status: err.response.status
});
}
return thunkAPI.rejectWithValue({
err: 'Network Error'
});
}
}
);
and below are the promises are handled
[action.pending]: state => ({
...state,
loading: true
}),
[action.fulfilled]: (state, action) => ({
...state,
data: {
...state.data,
loading: false,
list: action.payload.data.list
message: action.payload.data.message
}
}),
[action.rejected]: (state, action) => ({
...state,
loading: false
error: action.payload.data.error
message: action.payload.data.message
})
pending resolved and rejected all working well but in rejected i am unable to fetch the data but where action is dispatching thunkApi with error i am getting data there why not in rejected promise. any help would be appreciated thanks
You pass err to rejected but you forgot use it.
error: action.payload.err.data.error
message: action.payload.err.data.message
And you are declare thunkAPI wrong way. Just update like this:
async (data, thunkAPI) => {
const { getState } = thunkAPI;
}

How to make state iterable and resolve "Unhandled Rejection (TypeError): state.story is not iterable"?

I send a number of files and some other data via redux and axios to my backend. However, when I do so, I get the following error message Unhandled Rejection (TypeError): state.story is not iterable. Although the error occurs in my react js fronted, the data is successfully sent to my backend. When i send normal key vlaue pairs and no array it also works fine.
I now wonder how i can make state.story iterable? Or more general how to approach that issue?
// creating the form data
onClickUpload (e){
let files = this.state.image;
let formData = new FormData();
formData.append('title', "Some_Title")
for (var i = 0; i < files.length; i++) {
formData.append(`story_media[${i}]isTitlePicture`, files[i].isTitlePicture)
formData.append(`story_files[${i}]files`, files[i].file)
}
this.props.addStory(formData);
}
// ADD Story Action
export const addStory = formDataStory => (dispatch, getState) =>
new Promise((resolve, reject) => {
axios.post(apiBase +"/story/createupdatedestroy/", formDataStory, tokenConfig(getState) )
.then(res => {
dispatch(createMessage({ addStory: "Story Added" }));
dispatch({
type: ADD_STORY,
payload: res.data
});
resolve(res);
})
.catch(err => {
reject(err);
dispatch(returnErrors(err.response.data, err.response.status))
}
);
// Add STory Reducer
const initialState = {
story: [],
isFetching: "idle"
};
export default function (state = initialState, action) {
switch (action.type) {
case GET_STORY_REQUEST:
return {
...state,
isFetching: "loading"
};
case GET_STORY_SUCCESS:
return {
...state,
story: action.payload,
isFetching: "success"
};
case GET_STORY_FAILURE:
return {
isFetching: "failure"
};
case GET_SINGLE_STORY:
return {
...state,
story: state.story.filter(story => story.id !== action.payload)
};
case DELETE_STORY:
return {
...state,
story: state.story.filter(story => story.id !== action.payload)
};
case EDIT_STORY:
return {
...state,
story: state.story.filter(story => story.id !== action.payload)
};
case ADD_STORY:
return {
...state,
story: [...state.story, action.payload]
};
default:
return state;
}
}
Issue
One of your reducer cases is not copying the state object correctly. It neglects to copy current state into the new state object.
case GET_STORY_FAILURE:
return {
isFetching: "failure"
};
Solution
Copy existing state into the new state object.
case GET_STORY_FAILURE:
return {
...state,
isFetching: "failure"
};

Redux-thunk: API response keeps pending

I am trying to develop an application, that is showing photos from Unsplash given a keyword. I managed to fetch specific photos using unsplash.js. In my actions, I have several action creators:
export const fetchPhotos = () => ({
type: FETCH_PHOTOS
});
export const receivePhotos = term => {
const unsplash = new Unsplash({
applicationId:
"id",
secret: "secret",
callbackUrl: "callback"
});
console.log(term);
const response = unsplash.search
.photos(term, 1, 20)
.then(toJson)
.then(json => json)
.then(json => json)
console.log(response.then(results => results));
return {
type: RECEIVE_PHOTOS,
payload: response
};
}
export const unsplash = (term) => dispatch => {
console.log(term);
dispatch(fetchPhotos());
setTimeout(() => {
dispatch(receivePhotos(term));
console.log("dispatching")
return Promise.resolve();
}, 1000)
}
My reducers then do:
const initialState = {
isFetching: false,
sortDirection: null,
sortKey: null,
items: []
}
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_PHOTOS:
console.log(state, "Fetch photos reducer");
return {
...state,
isFetching: true
};
case RECEIVE_PHOTOS:
console.log("Receive photos reducer", action.payload)
return {
...state,
isFetching: false,
items: action.payload
};
case SET_SORT:
return {
...state,
sortKey: action.sortKey,
sortDirection: action.sortDirection
};
default:
return state;
}
}
However, as the receivePhotos action creator calls an API, I have a promise that needs to be resolved in order for the whole application to work. My fetch photos reducer is console logging the action, then the Promise appears, however it is always on pending. Then my receivePhotos action creator dispatches to the reducer and I can see that this is a Promise:
How can I resolve this promise?
In the below code you assign a promise to response, then console.log that promise, and then return the action with payload set to that promise.
const response = unsplash.search
.photos(term, 1, 20)
.then(toJson)
.then(json => json)
.then(json => json)
console.log(response.then(results => results));
return {
type: RECEIVE_PHOTOS,
payload: response
};
dispatch(receivePhotos(term)); then dispatches that action, still with the payload as a promise. maybe this would work if you had middleware that could handle it.
This use of dispatch suggests that you are using redux-thunk though.
In that case, you should do the same with receivePhotos, include the fetchPhotos call, and retire the unsplash action.
const unsplashClient = new Unsplash({
applicationId:
"id",
secret: "secret",
callbackUrl: "callback"
});
export const receivePhotos = term => dispatch => {
dispatch(fetchPhotos());
return unsplashClient.search
.photos(term, 1, 20)
.then(toJson)
.then(json => dispatch({
type: RECEIVE_PHOTOS,
payload: json
});
}
In the end I would suggest a bit of refactoring of the actions and the (related reducers) such as:
const unsplashClient = new Unsplash({
applicationId:
"id",
secret: "secret",
callbackUrl: "callback"
});
export const fetchingPhotos = payload => ({
type: FETCHING_PHOTOS, payload
});
export const setPhotos = payload => ({
type: SET_PHOTOS, payload
});
export const fetchPhotos = term => dispatch => {
dispatch(fetchingPhotos(true));
return unsplashClient.search
.photos(term, 1, 20)
.then(toJson)
.then(json => {
dispatch(setPhotos(json));
dispatch(fetchingPhotos(false));
});
}

Trying to return a promise from an action creator dispatched from mapDispatchToProps

I have an action creator like so.
export const register = user => {
return dispatch => {
dispatch(requestRegistation());
return new Promise((resolve, reject) => {
Axios.post(make_url('/users'), { user: user }).then(response => {
if (response.data.errors) {
reject(response.data.errors);
}
if (response.data.user) {
resolve(response.data.user);
}
});
});
};
};
and I have a form like so.
handleSubmit: (values, props) => {
props.props.register(values).then(
response => {
console.debug('response', response);
},
errors => {
console.debug('errors', error);
},
);
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
register: user => {
dispatch(register(user));
},
};
};
It would look as if I'm not returning a promise as I see this.
signup.js:186 Uncaught (in promise) TypeError: Cannot read property 'then' of undefined
signup.js:186 is relative to props.props.register(values).then(
I fell like I'm close but I'm not sure what I'm missing here?
You are not returning the promise from the register function in mapDispatchToProps. You can remove the function body to make the return implicit, or add a return statement.
const mapDispatchToProps = (dispatch, ownProps) => {
return {
register: user => dispatch(register(user)),
};
};

Firebase update method produces Promise Rejection

I'm making an update request on my React Native app to my Firebase with redux in mind.
Here's my redux snippet
export function buyTicket(eventID) {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/Users/${currentUser.uid}/joinedEvent`).update({ [eventID]: true })
.then(() => dispatch({ type: BUY_TICKET_SUCCESS }))
.catch(() => dispatch({ type: BUY_TICKET_FAIL }));
};
};
When the buyTicket function gets called, only the then() method should be expected but both then() and catch() got called.
According to the Firebase docs, update() produces a promise but its optional.
Here's the error I'm getting
This is my reducer
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case PULL_EVENT_DATA:
return action.payload;
case PULL_TRENDING_DATA:
return action.payload;
case BUY_TICKET_SUCCESS:
return {
message: 'Yay, see you there!'
}
case BUY_TICKET_FAIL:
return {
message: 'shit'
}
default:
return state;
}
}
Perhaps the console log might gives a clue?
As Jan pointed out the part I was missing, I was able to relocate the error and make it right.
The error I was making is mutability. I did not consider the concept of mutability when making the app, hence the error.
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case PULL_EVENT_DATA:
return action.payload;
case PULL_TRENDING_DATA:
return action.payload;
case BUY_TICKET_SUCCESS:
return { ...state, message: action.payload}
case BUY_TICKET_FAIL:
return { ...state, message: action.payload}
default:
return state;
}
}
You may find more about mutability here

Categories