bigpotato
bigpotato

Reputation: 27527

Redux Observable: How to return an action from a callback?

I'm using the WebRTC library which has a very specific API. The peerConnection.setRemoteDescription method's 2nd argument is supposed to be a callback for when it finishes setting the remote description:

This is one of my wrapper functions for my WebRTC class:

export function setRemoteSdp(peerConnection, sdp, callback) {
  if (!sdp) return;
  return peerConnection.setRemoteDescription(
    new RTCSessionDescription(sdp),
    callback, // <-------------
  );
}

And this is a sketch of what I want to do:

function receivedSdp(action$, store) {
  return action$.ofType(VideoStream.RECEIVED_SDP)
    .mergeMap(action => {
      const {peerConnection} = store.getState().videoStreams;
      const {sdp} = action.payload;

      return WebRTC.setRemoteSdp(peerConnection, sdp, () => {
        return myReducer.myAction(); // <------ return action as the callback
      })
    })
};

This doesn't work since I'm not returning an Observable. Is there a way to do this?

P.S. this is the WebRTC API: https://github.com/oney/react-native-webrtc/blob/master/RTCPeerConnection.js#L176

Upvotes: 3

Views: 2140

Answers (2)

jayphelps
jayphelps

Reputation: 15401

martin's answer is correct about using Observable.create or new Observable--same thing (except it's not clear to me why you need the mergeAll() since the mergeMap will flatten?)

As a bonus, you could also use Observable.bindCallback for this.

// bindCallback is a factory factory, it creates a function that
// when called with any arguments will return an Observable that
// wraps setRemoteSdp, handling the callback portion for you.
// I'm using setRemoteSdp.bind(WebRTC) because I don't know
// if setRemoteSdp requires its calling context to be WebRTC
// so it's "just in case". It might not be needed.
const setRemoteSdpObservable = Observable.bindCallback(WebRTC.setRemoteSdp.bind(WebRTC));

setRemoteSdpObservable(peerConnection, sdp)
  .subscribe(d => console.log(d));

Usage inside your epic would be something like this

// observables are lazy, so defining this outside of our epic
// is totally cool--it only sets up the factory
const setRemoteSdpObservable = Observable.bindCallback(WebRTC.setRemoteSdp.bind(WebRTC));

function receivedSdp(action$, store) {
  return action$.ofType(VideoStream.RECEIVED_SDP)
    .mergeMap(action => {
      const {peerConnection} = store.getState().videoStreams;
      const {sdp} = action.payload;

      return setRemoteSdpObservable(peerConnection)
        .map(result => myReducer.myAction());
    })
};

You could use this to create Observable wrappers for all the WebRTC apis.

Upvotes: 4

martin
martin

Reputation: 96949

So the problem is that setRemoteSdp doesn't return an Observable while myReducer.myAction() does and that's the Observable you want to merge?

You can use Observable.create and wrap the WebRTC.setRemoteSdp call:

.mergeMap(action => {
  return Observable.create(observer => {
    WebRTC.setRemoteSdp(peerConnection, sdp, () => {
      observer.next(myReducer.myAction());
      observer.complete();
    })
  });
}
.mergeAll()

The Observable.create returns an Observable that emits another Observable from myReducer.myAction(). Now I have in fact so-called higher-order that I want to flatten using mergeAll() (concatAll would work as well).

Upvotes: 3

Related Questions