redux-logic icon indicating copy to clipboard operation
redux-logic copied to clipboard

Question: is dispatching in cancelled$ observable subscriber OK?

Open zdila opened this issue 8 years ago • 3 comments

We need to dispatch action in cancelled$ observable subscriber. Is it OK? Our usecase is:

process({ cancelled$ }, dispatch, done) {
    const pid = Math.random(); // progress id
    dispatch(startProgress(pid));
    cancelled$.subscribe(() => {
      dispatch(stopProgress(pid));
    });
    asyncFn(function (err, result) {
      dispatch(setResult(err, result));
      dispatch(stopProgress(pid));
      done();
    });
}

Actions startProgress and stopProgress is to indicate some async profress and is presented in UI.

We tried it and it works.

Also documentaiton says:

This osbservable will also complete when the hooks have finished, regardless of whether it was cancelled.

What does it exactly mean? Because calling done() doesn't complete it (subscriber is not called).

zdila avatar Jun 27 '17 21:06 zdila

@zdila I don't believe that dispatching in the cancelled$ will work for you since once things are cancelled, I don't send anything that is dispatched after that.

What the documentation was trying to say is that cancelled$.next is fired when things are cancelled$ but also cancelled$.done fires when everything was complete. However in both cases the underlying observable is finished so the dispatch function won't send anything out.

Normally if I need to do something in regards to a cancellation, I simply listen for the event that triggers the cancellation and update the UI from that. So if a LOCATION_CHANGE triggers the cancel then I would have logic that listens for that and simply clears out the UI. However in your case you'd need to track the pids somehow, probably storing them in redux so they are all available when you want to handle the cancellation.

If you'd rather do it in a manner like you have here inside the logic, then you can do the following.

// in your create store function add a new injected dep after your create your logic mw and store
const logicMiddleware = createLogicMiddleware(logic);
const store = createStore(rootReducer, applyMiddleware(logicMiddleware));
store.logicMiddleware = logicMiddleware; // attach for easy use later
// now adding a new injected dep called storeDispatch which always will dispatch 
// even after a logic has been cancelled
store.logicMiddleware.addDeps({ storeDispatch: store.dispatch });

You have to add this injected dependency after you have created your store so you can get a hold of the dispatch fn, so that's why we added it with addDeps() rather than providing it during the createLogicMiddleware. It's a chicken and egg thing, need to create store to get access to the dispatch function.

Now that you have this new storeDispatch function as a dep you can also use it in your logic to do dispatching at any time even after cancellation or completion. You still use the original process dispatch when you want to have cancellation to stop your dispatches, but the new storeDispatch can be used when you always want to dispatch regardless.

So your logic adapted to use the new storeDispatch would look like this:

process({ cancelled$, storeDispatch }, dispatch, done) {
    const pid = Math.random(); // progress id
    dispatch(startProgress(pid));
    cancelled$.subscribe(() => {
      // need to use storeDispatch here since dispatch doesn't emit after cancellation
      storeDispatch(stopProgress(pid));
    });
    asyncFn(function (err, result) {
      dispatch(setResult(err, result));
      dispatch(stopProgress(pid));
      done();
    });
}

jeffbski avatar Jun 29 '17 12:06 jeffbski

Thanks for your solution, but...

I don't believe that dispatching in the cancelled$ will work for you since once things are cancelled, I don't send anything that is dispatched after that.

...it really works :-)

cancelType: ['SET_TOOL'],
cancelled$.subscribe(() => {
  dispatch(stopProgress(pid));
});

... and in the logs I see dispatching SET_TOOL and immediately after that STOP_PROGRESS (using redux-logger).

zdila avatar Jun 29 '17 22:06 zdila

Maybe it is occurring immediately before the cancellation is actually taking effect. If it works, it works :-) If for any reason it doesn't you can use the above solution.

jeffbski avatar Jul 05 '17 15:07 jeffbski