6

In my reactjs redux application I'm using redux observable epics middleware for ajax requests to cancel some requests.

const epicRequest = $actions => {
  return $actions
         .ofType(MY_ACTION_TYPE)
         .debounceTime(500)
         .switchMap( action =>{
            // Doing Ajax
          })
} 

My problem is I call this action when I press some buttons, I have two kinds of buttons both can call the same action, in that case if call from first action and second action is different button I don't want to debounce, I want to debounce only if the action is from same button.

Kamaal ABOOTHALIB
  • 3,527
  • 1
  • 19
  • 25

2 Answers2

4

To fix this you have to pass additional prop from your button call and check that prop from your epic.

Inside your component button click call, pass your button identification.

onclick = () => {
  // assume arg name is 'from'
  this.props.yourAction('a') // can be 'a'/'b' ...
}

In your action call, pass the value that you have got from button call.

const yourAction = (from) => {
  return { type: MY_ACTION_TYPE, from }
}

In reducer switch case

case MY_ACTION_TYPE:
  return {...state, from:action.from}

Finally in your epic middleware function, replce .debounceTime(600) with .debounce(), with this you can pass function as argument that returns Observable.timer(). So you can check your prop here and return the debounce time.

let from = null
const epicRequest = $actions => {
    return $actions
        .ofType(MY_ACTION_TYPE)
        //.debounceTime(500)
        .debounce((action) => {
            // check button type is not yet set or not previous type
            const  timer = !from || (from !== action.from)  ? Observable.timer(0) : Observable.timer(600)
            // update button type
            from =  (from !== action.from) ? action.from : from
            // return Observable.timer()
            return timer
        })
        .switchMap( action =>{
            // Do your Ajax
        })
}
Kamaal ABOOTHALIB
  • 3,527
  • 1
  • 19
  • 25
0

It's not clear whether you mean you want them both debounced, but independently, or if you only want one of them to have debouncing at all. I've included answers to both:

2+ buttons independently debounced

I highly advise having them emit different action types, if they have different intents. If the intents are the same, e.g. the same button repeated in list items, then one way to handle this would be to use groupBy to group each button independently by some unique key that button always includes.

const epicRequest = action$ =>
  action$
    .ofType(MY_ACTION_TYPE)
    .groupBy(action => action.somethingUnique)
    .mergeMap(action$ =>
      action$
        .debounceTime(500)
        .switchMap(action => {
          // Doing Ajax
        })
    );

This example assumes that every button includes something that uniquely identifies it, included as somethingUnique.

Some resources on groupBy

WARNING: by default groupBy never complete()'s or otherwise cleans up every group Observable it creates, which means this potentially is a memory leak. Whether that matters in your app or not, only you can know. If it does, groupBy supports a third argument duration selector that you can use to decide when to stop tracking a particular group--that doesn't mean it create a new one if its needed later, just that the current Observable representing it is complete()'d. Often this is tough to do cleanly in practice without some sort of explicit teardown signal, like a route navigation action away from the page. See the docs for more info: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-groupBy durationSelector: function(grouped: GroupedObservable<K, R>): Observable<any>

1 button debounced, another button never

This means they have different intents. One is intended to be debounced, the other not, even if after the debouncing their actions are the same.

The action that needs debouncing can emit a totally different action like MY_ACTION_TYPE_DEBOUNCED, then after debouncing emit the normal MY_ACTION_TYPE

const myActionTypeDebouncedEpic = action$ =>
  action$
    .ofType(MY_ACTION_TYPE_DEBOUNCED)
    .debounceTime(500)
    .map(action => ({
      ...action,
      type: MY_ACTION_TYPE
    }));

const myActionTypeEpic = action$ =>
  action$
    .ofType(MY_ACTION_TYPE)
    .switchMap(action => {
      // Doing Ajax
    });
jayphelps
  • 15,276
  • 3
  • 41
  • 54
  • I tried first answer, it's works for me, Im new to Rx, can you please explain me which one is the better solution? – Bishrul hafi Feb 05 '18 at 07:38
  • @Bishrulhafi they have different behaviors and use cases. I included both because it wasn't clear which you intended. FWIW I've never personally needed to independently debounce buttons with the same intent (first example with groupBy). – jayphelps Feb 05 '18 at 07:41