feerlay
feerlay

Reputation: 2638

Redux Observable: dispatch action on multiple clicks (two or more)

I'm using redux observable as a middleware for redux to deal with side effects. I want to dispatch action A only when action B was emitted more than twice in some specified period of time (say 500ms).

My attempt for this: demo

Here is how epic looks:

const pingEpic = action$ =>
  action$
    .buffer(action$.ofType(CLICK).throttleTime(500))
    .map(x => x.length)
    .filter(x => x >= 2)
    .mapTo({ type: PING });

This epic correctly accumulates clicks in lists and filters those that are longer than 2, but the PING action is dispatched after another additional click.

Upvotes: 1

Views: 571

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23473

I find it easier to nut out complicated rxjs by breaking it down into smaller bits.

So, you want to PING on a double-click and PONG on a single-click, and CLICK is the only source of events.

double-click Ref

const doubleClick = action$ =>
  action$.ofType(CLICK)
    .buffer(action$.ofType(CLICK).debounceTime(500))
    .map(x => x.length)
    .filter(x => x === 2)
    .mapTo({ type: PING });

single-click

const singleClick = action$ =>
  action$.ofType(CLICK)
    .buffer(action$.ofType(CLICK).debounceTime(500))
    .map(x => x.length)
    .filter(x => x === 1)
    .mapTo({ type: PONG });

PING/PONG

const pingEpic = action$ =>
  Rx.Observable.merge(
    singleClick(action$), 
    doubleClick(action$)
  )

Note, it seems to work with straight replace of throttleTime with debounceTime,

const pingEpic = action$ =>
  action$
    .buffer(action$.ofType(CLICK).debounceTime(500))
    .map(x => x.length)
    .filter(x => x >= 2)
    .mapTo({ type: PING });

but you don't get any PONGs happening with this. (Adding a console.log to the reducer shows the flow a bit better)

const pingReducer = (state = { isPinging: 'NO' }, action) => {
  console.log('reducer', action.type)
  ...

Here's the example Fiddle

Upvotes: 1

Related Questions