redux-observable epic chain dispatches duplicate actions

Legend:

action-chain epic = Epic, which returns 'success' or 'fail' action, based on service call. service = Http service, from HttpModule, Angular2

The goal

I need to send data to server. I have a CREATE, CREATE_SUCCESS and CREATE_FAIL actions. My idea is to dispatch CREATE on submit. Then I have an action-chain epic, which starts service call to my server and fires CREATE_SUCCESS or CREATE_FAIL depending on the response.

The problem

My action-chain epic fires duplicate actions - in both cases it fires two actions, eg: in case of success, for every CREATE it dispatches two CREATE_SUCCESS actions.

The code

 private createAnimalActionChain() {
        return action$ => {
            return action$
                .ofType(AnimalActions.CREATE)
                .switchMap(action => {
                    return this.service
                        .create(action.payload)
                        .map(httpResponse => {
                            let response = httpResponse.json()
                            if (response.success) {
                                return this.actions.createSuccess()
                            }

                            return this.actions.createFail(response.errors)
                        })
                })
        }
    }

This is what I composed and it's working for the most part. If there is a better approach, I am all ears. Dispatching multiple actions was not real problem until recently, but it never seemed ok with me, so I'd love to fix it.

EDIT: I have just found out that sometimes my app dispatches duplicate actions before any Epic is executed. I am now trying to understand why.

EDIT 2: Nevermind EDIT. It was an isolated case, where submit event was bubbling all the way to the top, and because I named my @Output() field 'submit' - it caused double action dispatch. It is fixed now and is not relevant to the Epic issue, described above. It still persists.

EDIT 3: With the help of @jayphelps, I was able to track the issue. It turns out my actions, called from the epic chain, triggered twice. I am not sure why that happens though. Here is my old code:

static CREATE = 'CREATE'
static CREATE_SUCCESS = 'CREATE_SUCCESS'
static CREATE_FAIL = 'CREATE_FAIL'
@dispatch()
create = (animal: IAnimal) => ({
    type: AnimalActions.CREATE,
    payload: animal
})
@dispatch()
createSuccess = () => ({
    type: AnimalActions.CREATE_SUCCESS
})
@dispatch()
createFail = (errors: object) => ({
    type: AnimalActions.CREATE_FAIL,
    payload: errors
})

This code dispatches CREATE once, CREATE_SUCCESS twice and CREATE_FAIL twice. I removed the last two @dispatch() decorators:

static CREATE = 'CREATE'
static CREATE_SUCCESS = 'CREATE_SUCCESS'
static CREATE_FAIL = 'CREATE_FAIL'
@dispatch()
create = (animal: IAnimal) => ({
    type: AnimalActions.CREATE,
    payload: animal
})
createSuccess = () => ({
    type: AnimalActions.CREATE_SUCCESS
})
createFail = (errors: object) => ({
    type: AnimalActions.CREATE_FAIL,
    payload: errors
})

This code seems to work properly and only fire one of each action. I am very confused as to why this works like that. According to API docs this property decorates a single action-creator function and dispatches its return value, an action object. it is very weird, because I call CREATE and CREATE_SUCCESS identically. How does this decorator work?

EDIT 4: I have narrowed the behavior down to this: If I call an action from within my Epic and this action is decorated with @dispatch(), it is triggered twice. I suspect that the Epic, being a middleware internally dispatches the received action. The problem comes, if I need to manually dispatch an action from my container component.

1 answer

  • answered 2017-08-12 20:11 jayphelps

    The code provided is fine; see here, only one CREATE_SUCCESS: https://jsbin.com/tagovok/edit?js,output

    This suggests the problem is in code not provided here. Three immediate possibilities I can think of:

    • Somewhere, somehow you're dispatching two CREATE actions (use redux devtools to see)
    • this.service.create(action.payload) actually emits two things instead of one (use do, debuggers, or you could even just add a .take(1) after it and if that fixes it you know)
    • You have the same epic added twice, e.g. combineEpics(yourEpic, yourEpic) (this one is hard to confirm. Maybe try adding a counter to the initial call of the epic it and confirming it only gets incremented to 1 since the epic function itself is only ever called once)

    Update based on yours:

    I have narrowed the behavior down to this: If I call an action from within my Epic and this action is decorated with @dispatch(), it is triggered twice. I suspect that the Epic, being a middleware internally dispatches the received action. The problem comes, if I need to manually dispatch an action from my container component.

    Indeed redux-observable will automatically dispatch actions emitted by your Epics. I don't have experience using angular-redux, but there is some docs on using it with redux-observable here: https://github.com/angular-redux/store/blob/master/articles/epics.md

    A quick skim suggests they do not use the action creators in the epics and instead just use POJO. You could still create action creators that only return the POJO actions, just normal redux.