简体   繁体   中英

In Rx.js, how can I distinguish which stream triggers the combineLatest method?

I'm writing my own version of who to follow? . Clicking refreshButton will fetching suggestions list and refresh <Suggestion-List /> , and closeButton will resue the data from suggestions list and refresh <Suggestion-List-Item /> .

I want to let the closeClick$ and suggestions$ combine together to driving subscribers. Demo code here:

var refreshClick$ = Rx.Observable
  .fromEvent(document.querySelector('.refresh'), 'click')

var closeClick$ = Rx.Observable.merge(
  Rx.Observable.fromEvent(document.querySelector('.close1'), 'click').mapTo(1),
  Rx.Observable.fromEvent(document.querySelector('.close2'), 'click').mapTo(2),
  Rx.Observable.fromEvent(document.querySelector('.close3'), 'click').mapTo(3)
)

var suggestions$ = refreshClick$
  .debounceTime(250)
  .map(() => `https://api.github.com/users?since=${Math.floor(Math.random()*500)}`)
  .startWith('https://api.github.com/users')
  .switchMap(requestUrl => Rx.Observable.fromPromise($.getJSON(requestUrl)))

Rx.Observable.combineLatest(closeClick$, suggestions$, (closeTarget, suggestions) => {
  if (/* the latest stream is closeClick$ */) {
    return [{
      target: clickTarget,
      suggestion: suggestions[Math.floor(Math.random() * suggestions.length)]
    }]
  }
  if (/* the latest stream is suggestions$ */) {
    return [1, 2, 3].map(clickTarget => ({
      target: clickTarget,
      suggestion: suggestions[Math.floor(Math.random() * suggestions.length)]
    }))
  }
})

Rx.Observable.merge(renderDataCollectionFromSuggestions$, renderDataCollectionFromCloseClick$)
  .subscribe(renderDataCollection => {
    renderDataCollection.forEach(renderData => {
      var suggestionEl = document.querySelector('.suggestion' + renderData.target)
      if (renderData.suggestion === null) {
        suggestionEl.style.visibility = 'hidden'
      } else {
        suggestionEl.style.visibility = 'visible'
        var usernameEl = suggestionEl.querySelector('.username')
        usernameEl.href = renderData.suggestion.html_url
        usernameEl.textContent = renderData.suggestion.login
        var imgEl = suggestionEl.querySelector('img')
        imgEl.src = "";
        imgEl.src = renderData.suggestion.avatar_url
      }
    })
  })

You can find it in JsFiddle .

You should note the comments in condition judgment, closeClick$ emits [{ target: x, suggestion: randomSuggestionX }] , suggestions$ emits [{ target: 1, suggestion: randomSuggestion1 }, { target: 2, suggestion: randomSuggestion2 }, { target: 3, suggestion: randomSuggestion3 }] . Subsriber render interface according to the emitted data.

May there are some ways/hacks to distinguish the latest stream in combineLatest or elegant modifications?

I think the easiest way would be to use the scan() operator and always keep the previous state in an array:

Observable.combineLatest(obs1$, obs2$, obs3$)
  .scan((acc, results) => {
    if (acc.length === 2) {
      acc.shift();
    }
    acc.push(results);
    return acc;
  }, [])
  .do(states => {
    // states[0] - previous state
    // states[1] - current state
    // here you can compare the two states to see what has triggered the change
  })

Instead of do() you can use whatever operator you want of course.

Or maybe instead of the scan() operator you could use just bufferCount(2, 1) that should emit the same two arrays... (I didn't test it)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM