I'm currently working on implementing a sophisticated alert system using redux and redux-observable.
The requirements are:
- An action should request an alert: REQUEST_ALERT
- An action should create an alert and add an ID: SET_ALERT (handled in the epic)
- An alert should automatically close after 3 seconds: DISMISS_ALERT (handled in the epic)
- An alert can be manually closed by a user click: DISMISS_ALERT
(We could potentially refactor to directly use SET_ALERT without REQUEST_ALERT inside the reducer, but that's not the main issue at hand here).
I've made significant progress with the following code snippet:
// Generate the alert from the request
// This section may be ignored if refactored to only use REQUEST_ALERT
export const generateAlert$ = (action$) => {
return action$.ofType(REQUEST_ALERT)
.map(action => ({
type: SET_ALERT,
payload: generateAlertPayload(),
// Simply creating an action of type SET_ALERT and adding an ID
})
)
}
// The crucial part of the implementation
export const timeoutDismissAlert$ = (action$) => {
return action$.ofType(SET_ALERT)
.mergeMap(action =>
Rx.Observable.empty()
.concat(
// Wait for the maximum delay before sending the dismiss_alert payload
Rx.Observable.of(action)
.delay(maxDelay)
.map(({payload}) => ({type: DISMISS_ALERT, payload: payload.id}))
// Create a race between the user click and the delay
.race(
action$.ofType(DISMISS_ALERT)
.skipWhile(({payload}) => payload !== action.payload.id)
.first()
.map(() => null)
)
)
// Dispatch an action only if the delay wins
.map((a) => a ? a : {type: "I_DONT_WANT"})
)
}
Is there a way to avoid dispatching an action at the end of an epic? Or is there a more efficient RxJS approach to achieve this?