@xavier7179 Is there any reason you can't move this code to another epic?
action$.ofType(FETCH_EPIC2).pipe(
take(1),
map(() => fetchEPIC2(original_action.payload))
I know you need original_action.payload
. When your action completes, can you add originalActionPayload: original_action.payload
to the action?
@xavier7179 Is there any reason you can't move this code to another epic?
Sorry, I'm not sure I got the question...
I know you need
original_action.payload
. When your action completes, can you addoriginalActionPayload: original_action.payload
to the action?
I can certainly add it. Is it the issue I'm facing figuring out how to chain several epics in sequence, avoiding parallelism?
I'm not sure if nesting is the right approach. what about putting all the epics at the top level? Start the second epic with the success action of the first. Have a separate epic to further process the errors or combine the logic into a custom operator
But this way I cannot call epics alone... am I right?
`const createStoreWithMiddleware = applyMiddleware('
`promiseMiddleware,`
`ReduxThunk`
`)(createStore);`
`ReactDOM.render(`
`<Provider`
` store={createStoreWithMiddleware(`
` Reducer,`
` window.__REDUX_DEVTOOLS_EXTENSION__ &&`
` window.__REDUX_DEVTOOLS_EXTENSION__()`
` )}`
` >`
` <BrowserRouter>`
` <App />`
` </BrowserRouter>`
`</Provider>,`
@evertbouw @Sawtaytoes I actualy solved 90% of the issue with the following two epics:
export function fetchAllEpics(action$) {
return action$.pipe(
ofType(FETCH_ALL_EPICS),
take(1),
mergeMap((original_action) =>
concat(
of(fetchFirstEpicAction(original_action.payload)),
action$.ofType(EPIC1_FETCHED).pipe(
take(1),
mergeMap(() =>
concat(
of(fetchSecondEpicAction(original_action.payload)),
action$.ofType(EPIC2_FETCHED).pipe(
take(1),
map(() => fetchThirdEpicAction(original_action.payload))
)
),
)
)
)
)
);
}
export function collectEpicResults(action$) {
return action$.pipe(
ofType(FETCH_ALL_EPICS),
mergeMap((action) =>
combineLatest([
action$.ofType(EPCI1_FETCHED),
action$.ofType(EPIC2_FETCHED),
action$.ofType(EPIC3_FETCHED)
]).pipe(
map(([e1, e2, e3]) =>
fetchAllEpicsFullfilled({ e1: e1.payload, e2: e2.payload, e3: e3.payload })
),
catchError(([error_e1, error_e2, error_e3]) => of(fetchAllEpicsFailed([error_e1, error_e2, error_e3])))
)
)
);
}
where the composition can be done by replacing the map
with a mergeMap
+ concat
block. Everything works fine in the serialization, while the second epic is perfect when everything works without errors. When in the chain some epic fails (notice that each EPIC has its own piping of map and catchError) then, the first epic is stuck. I figure out that maybe the issue is not having two epics but putting all in one epic but... I did not work out how, in order to keep the correct path still fully working.
@andrevmatos That might be something in your code. I've run really complex Redux-Observable applications and performance tests. It's more-likely something where your pipelines are spawning more observables and not getting rid of past ones. Or that the pipelines are computationally intensive.
Is it possible to post any of that code?
from
with scheduled
+ asapScheduler
on some epics which could dispatch intensive side-effects actions did seem to have fixed or worked around the issue, at least preventing other epics from starving. raiden-network/light-client#2281
@andrevmatos Awesome! Glad you were able to fix it! And yes, you are right, using concatMap
is running things sequentially rather than async. Same issue people run into using async-await
.
Redux-Observable has its own scheduler to allow things to speed up, but I'm assuming you needed something even more than that in your scenario. Personally, I'm curious.
Similar to you, I also wrote my own controller for my LIFX lights all in Redux-Observable :).
@JesusTheHun I don't pass callbacks like that anywhere in Redux-Observable , but sometimes I will pass classes with methods if necessary like a flicClient
or eventEmitter
depending.
While it's possible to pass React callbacks to Redux-Observable, that's really strange and wouldn't work if you put your Redux store in a Web Worker or a child process.
The way of doing it is creating a generic epic or epics that work with any form. Add some form of id
and now you can identify form to close regardless of which epic you're in.
Your Modal
component, or whatever you're using to display the form would get hidden because it's listening to the open state of that particular ID.
Does this make sense? If you have code, paste some in, we can help you come up with a plan.
Hi @Sawtaytoes it does make sense, we actually have though about that option and then thought it was a bit overkill. Your worker argument is very good.
The way of doing it is creating a generic epic or epics that work with any form. Add some form of id and now you can identify form to close regardless of which epic you're in.
Why/How a generic epic ? In your business epic you dispatch your success action and in your generic epic you catch all those success/failure and your react to them ?
@PogoniiA The answer in that StackOverflow link is correct.
Because Firebase doesn't have a native RxJS implementation, you need to take their streaming function, wrap it in an RxJS observable, and pipe off that in Redux-Observable.
From there, it just dispatches actions and the rest of your code works as you'd expect.
Hello guys,
I'm trying to test my Epic, I'm quite new with TestScheduler and Marble Diagrams.
I hope someone can help.
this is my Epic:
const deleteMediaEpic: Epic = (actions$, state$: StateObservable, { api }: EpicDependencies) =>
actions$.pipe(
ofType(DELETE_MEDIA),
switchMap((action: DeleteMediaAction) => api.deleteMedia(action.payload.operationCode, action.payload.productKey, action.payload.mediaToDelete).pipe(
switchMap(() => {
const actionsToDispatch = []
actionsToDispatch.push(productSelected(action.payload.productKey, action.payload.operationCode))
actionsToDispatch.push(clearMediaSelected([]))
return of(...actionsToDispatch)
}),
catchError(mediaFetchError => of(`Error in deleteMediasEpic: ${mediaFetchError}`))
))
)
and this is my test:
it('Delete select Media', () => {
testScheduler.run(({ hot, cold, expectObservable }) => {
const action$ = hot('-a', {
a: { type: 'DELETE_MEDIA', payload: { productKey: 'PRODUCT_KEY1', operationCode: 'VPV_H_SHANAOR1', mediaToDelete: [0] } }
})
const state$ = {}
const dependencies = {
getJSON: url => cold('--a', {
a: { url }
})
}
const output$ = deleteMediaEpic(action$, state$, dependencies)
expectObservable(output$).toBe('---bc', {
b: {
type: 'PRODUCT_SELECTED',
payload: { productKey: 'PRODUCT_KEY1', saleCode: 'VPV_H_SHANAOR1', filtersType: undefined }
},
c: {
type: 'CLEAR_MEDIA_SELECTED',
payload: {
defaultValue: []
}
}
})
})
})
for me of
is a sequence operator and c should be immediately after b.
Hope you can understand something from the code me.
and this is the result of the test:
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
@@ -1,8 +1,8 @@
Array [
Object {
- "frame": 3,
+ "frame": 6,
"notification": Notification {
"error": undefined,
"hasValue": true,
"kind": "N",
"value": Object {
@@ -14,11 +14,11 @@
"type": "PRODUCT_SELECTED",
},
},
},
Object {
- "frame": 4,
+ "frame": 6,
"notification": Notification {
"error": undefined,
"hasValue": true,
"kind": "N",
"value": Object {
5 |
6 | const testScheduler = new TestScheduler(
> 7 | (actual, expected) => { expect(actual).toEqual(expected) }
| ^
8 | )