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.