barndog
barndog

Reputation: 7173

Re-invoke function that returns Rxjs observable

I'm building a carousel component in React and I want the consumable functions (things like advanceSlide, rewindSlide, etc) to return RxJS Observables they easily can be chained, merged, whatever with other observables.

As it stands now, my advance function looks like:

advance = (toIndex = this.state.focusedIndex + 1, animated = true) => {
    const destinationIndex = Math.max(0, Math.min(toIndex, this.props.children.length));
    const itemPosition = this._positions[destinationIndex];
    const domNode = $(ReactDOM.findDOMNode(this.refs.track));
    const animate = Rx.Observable.fromCallback(domNode.animate.bind(domNode));
    this.setState({ focusedIndex: destinationIndex });
    return animate({
      top: '-' + itemPosition.top + 'px'
    }, animated ? this.props.speed : 0);
};

Ideally I want to call this function like:

this.refs.carousel.advance().delay().repeat(); //achieve infinite carousel

However, and quite obviously, the observable returned from advance uses the same calculations upon repeat (since the function is invoked once with .advance() and just the observable is repeated.

What I need is a way to return an observable that runs the animation calculations again and then returns a new observable every time it's repeated.

To better illustrate what I want to achieve:

 carousel.advance().repeat(1).subscribe();

On the first invocation, the position and whatnot are calculated, the animation observable is returned, it runs, and everything animates correctly. On the second invocation, the signal is repeated with the same values so to the user, it appears as though nothing happens because the advance function hasn't been reinvoked.

How do I achieve that functionality using RxJS? I've been looking into using Rx.Observable.spawn but it doesn't seem to be what I need.

Upvotes: 1

Views: 507

Answers (1)

user3743222
user3743222

Reputation: 18665

The advance function is not reinvoked indeed but the observable that it outputs is resubscribed to.

Also note that you should use repeat(2) if you want to repeat once (I know it sounds strange).

If you want to repeat advance why don't you put it inside the observable chain? For example :

var Xms = 1000; // 1s
advanceRepeated = (context) => {
  return Rx.Observable.just(context)
                      .flatMap ((context) => {
                                 return context.refs.carousel.advance().delay(Xms); 
                               })
                      .repeat(2)
}

This is untested, so let me know if it indeed worked and please keep an eye for possible syntax errors.

Upvotes: 2

Related Questions