reactjs - Promise.catch in redux middleware being invoked for unrelated reducer -
i have following middleware use call similar async calls:
import { callapi } '../utils/api'; import generateuuid '../utils/uuid'; import { assign } 'lodash'; export const call_api = symbol('call api'); export default store => next => action => { const callasync = action[call_api]; if(typeof callasync === 'undefined') { return next(action); } const { endpoint, types, data, authentication, method, authenticated } = callasync; if (!types.request || !types.success || !types.failure) { throw new error('types must object request, success , failure'); } function actionwith(data) { const finalaction = assign({}, action, data); delete finalaction[call_api]; return finalaction; } next(actionwith({ type: types.request })); return callapi(endpoint, method, data, authenticated).then(response => { return next(actionwith({ type: types.success, payload: { response } })) }).catch(error => { return next(actionwith({ type: types.failure, error: true, payload: { error: error, id: generateuuid() } })) }); };
i making following calls in componentwillmount
of component:
componentwillmount() { this.props.fetchresults(); this.props.fetchteams(); }
fetchteams
example dispatch action handled middleware, looks this:
export function fetchteams() { return (dispatch, getstate) => { return dispatch({ type: 'call_api', [call_api]: { types: teams, endpoint: '/admin/teams', method: 'get', authenticated: true } }); }; }
both success actions dispatched , new state returned reducer. both reducers same , below teams
reducer:
export const initialstate = map({ isfetching: false, teams: list() }); export default createreducer(initialstate, { [actiontypes.teams.request]: (state, action) => { return state.merge({isfetching: true}); }, [actiontypes.teams.success]: (state, action) => { return state.merge({ isfetching: false, teams: action.payload.response }); }, [actiontypes.teams.failure]: (state, action) => { return state.merge({isfetching: false}); } });
the component renders component dispatches action:
render() { <div> <autocomplete items={teams}/> </div> }
autocomplete dispatches action in componentwillmount
:
class autocomplete extends component{ componentwillmount() { this.props.dispatch(actions.init({ props: this.exportprops() })); }
an error happens in autocomplete reducer invoked after success reducers have been invoked fetchteams
, fetchresults
original calls in componentwillupdate
of parent component reason catch handler in middleware first code snippet invoked:
return callapi(endpoint, method, data, authenticated).then(response => { return next(actionwith({ type: types.success, payload: { response } })) }).catch(error => { return next(actionwith({ type: types.failure, error: true, payload: { error: error, id: generateuuid() } })) }); };
i not understand why catch handler being invoked have thought promise has resolved @ point.
am not sure, it's hard debug reading code. obvious answer because it's happening within same stacktrace of call next(actionwith({ type: types.success, payload: { response } }))
.
so in case:
- middleware: dispatch
fetchteam
success insidepromise.then
- redux update props
- react: render new props
- react: componentwillmount
- react: dispatch new action
if error occurs @ point, bubble promise.then
, makes execute promise.catch
callback.
try calling autocomplete fetch inside settimeout
let current stacktrace finish , run fetch in next "event loop".
settimeout( () => this.props.dispatch(actions.init({ props: this.exportprops() })) );
if works, its' fact event loop hasn't finished processing when error occurs , middleware success dispatch way autocomplete rendered function calls after function calls.
note: should consider using redux-loop, or redux-saga asynchronous tasks, if want keep using custom middleware maybe can inspiration libraries on how make api request async initial dispatch.
Comments
Post a Comment