5

So basically I am dispatching an action with thunk and redux-promise-middleware, which makes an API call that returns a promise. I then send the promise returned to another action creator as a 'payload' argument, which works with the redux-promise-middleware and handles different actions MY_ACTION_TYPE_PENDING or MY_ACTION_TYPE_REJECTED or MY_ACTION_TYPE_FULFILLED. My question is do I handle the errors in reducer via the _REJECTED action and not catch it on my dispatch(actionCreator(payload)? When I do not catch the error on my dispatch I get a warning in the console, despite my reducer handling the error well with the _REJECTED ACTION.

Below are some of my actions:

export const RECEIVE_POSTS = 'RECEIVE_POSTS';
export const receivePosts = (data) => ({
    type: RECEIVE_POSTS,
    payload: data
})

// thunk middleware for fetching blog
                    export const fetchPosts = () => { 
                        return (dispatch) => {
                            const payload = contentfulClient.getEntries().then(
                                data => data.items,
                                error => console.log('An error occurred in fetchPost thunk middleware', error)
                                ) 
                            return dispatch(receivePosts(payload))
                            .catch((error) => {
                                console.log('caught error in fetchPost', error)
                            })
                        }
                    }

then this is some of my blog reducers file, it handles the actions the promise middleware sends out

const status = (state = Status.IDLE, action) => {
    switch (action.type) {
        case `${RECEIVE_POSTS}_PENDING` : 
            return Status.PENDING;      
        case `${RECEIVE_POSTS}_FULFILLED`:
            return Status.FULFILLED;
        case `${RECEIVE_POSTS}_REJECTED`:
            return Status.REJECTED;
        default:
            return state
    }
}

const error = (state = null, action) => {
    switch (action.type) {
    case `${RECEIVE_POSTS}_REJECTED`: 
        return action.payload.message
    default:
        return state;
    }
}
Patrick Burtchaell
  • 4,112
  • 1
  • 14
  • 25
john-raymon
  • 306
  • 1
  • 7
  • 20
  • 1
    why do you dispatch only 1 action in your action creator: `{type: 'RECEIVE_POSTS', payload: Promise_of_items}`? the reducer seems to expects 2 actions: `{type: 'RECEIVE_POSTS_PENDING'}` and then `{type: 'RECEIVE_POSTS_FULFILLED', payload}` but it's not doing anything with the `payload`, so hard to tell if your real code expects a Promise of `items` or actual `items` object (you would need to `dispatch` inside `then` for the latter case) – Aprillion Aug 18 '18 at 16:49
  • 1
    @Aprillion The redux-promise-middleware concats the action.type with some words either '_REJECTED', '_PENDING', or '_FULFILLED'. – john-raymon Aug 18 '18 at 16:55

2 Answers2

4

This is a good question and I don't think there is one answer. Ultimately, it is up to the developer or development team. As a practice, I would argue, yes, promise errors should be handled/caught at the dispatch. Here's why...

In your example, you don't catch the promise error. You, as you explained, only handle the error in your reducer.

case `${RECEIVE_POSTS}_REJECTED`:
            return Status.REJECTED;

You read an object with the type ${RECEIVE_POSTS}_REJECTED and write changes to the state. When you write changes to the state, you (presumably update UI and/or dispatch side effects to handle the error. This is a typical implementation for Redux.

The actual promise, however, remains uncaught in this implementation. In order to catch the promise error, you need to do so at dispatch (or in a middleware).

dispatch(myAsyncActionCreator()).catch(function(error) {
  // do something with the error
})

If you catch the error at dispatch, you won't see an error in the console. Verbose, yet straightforward/explicit, this practice makes it clear to other developers how errors are handled. I believe clarity is important for maintainability and future changes, hence why I argue for catching errors at dispatch.

Hope that helps!

Patrick Burtchaell
  • 4,112
  • 1
  • 14
  • 25
  • 2
    Thank you for your answer! This cleared a lot of things up, especially hearing it from another developer. I think in my code example above I caught the error in the dispatch in the thunk. `return dispatch(receivePosts(payload)) .catch((error) => { console.log('caught error in fetchPost', error) })` Is this what you mentioned ? – john-raymon Aug 18 '18 at 16:59
3

I find it easier to reason about following redux-thunk code compared to the redux-promise-middleware automagical type modifications... so posting as an alternative, in case other people find the comparison useful too:

export const fetchPosts = () => (dispatch) => {
  dispatch({type: '${RECEIVE_POSTS}_PENDING'})
  contentfulClient.getEntries()
    .then(data => dispatch({type: '${RECEIVE_POSTS}_FULFILLED', payload: data.items})
    .catch(error => dispatch({type: '${RECEIVE_POSTS}_REJECTED', payload: error})})
}
Aprillion
  • 21,510
  • 5
  • 55
  • 89
  • You're definitely right, I would've benefited from something like this when I didn't know much about redux-promise-middleware – john-raymon Aug 18 '18 at 17:09