4

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.

The solution

With the help of jayphelps. Thanks for your response. However I like the @dispatch decoration, for use inside of my components. A presonal solution I found is structuring my action chain in a manner that starter-actions (those that dispatch from inside a component) do not get dispatched via Epic. Then just decorate those starter-actions with @dispatch and it works.

Community
  • 1
  • 1
Alex
  • 715
  • 1
  • 8
  • 29

1 Answers1

6

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.

jayphelps
  • 15,276
  • 3
  • 41
  • 54
  • @alex you can try with the provided help, and if it's still doesn't solve your problem, than I suggest that you update your answer with the missing details – eenagy Aug 13 '17 at 01:13
  • Thanks for the input. I ran through those thoughts and the only potential issue I found is using the `@dispatch()` decorator from `angular-redux/store`. I must be dispatching the duplicates. I have not registered excess epics, I dispatch single `CREATE` action (confirmed via Redux dev tools). I will try to dispatch in a regular way. – Alex Aug 13 '17 at 01:27
  • Glad to hear it! – jayphelps Aug 14 '17 at 22:53
  • I am also facing the same issue. My **epic** dispatches the _same action_ twice. While In my case that happens only in my _tests_ for the **epic**. Debugging my app using `redux-dev-tools` show only one action dispatched. FYI, I am consuming the redux store in a **React** app. – besrabasant Feb 19 '18 at 06:33
  • @jayphelps Can you help me with my issue? Why am I getting two duplicate actions? Shall I create a separate question for this issue? – besrabasant Feb 19 '18 at 13:17
  • @besrabasant do you have a Stack Overflow question? – jayphelps Feb 19 '18 at 18:50
  • @jayphelps No, but I have solved the issue. It seems that there was a GitHub issue for this which I was unable to find. And I got the solution from your comment on that issue. https://github.com/redux-observable/redux-observable/issues/389#issuecomment-354623405 – besrabasant Feb 19 '18 at 23:45