15

The example and explanation of the let operator (https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/let.md) is not clear. Anyone has a good example/explanation how the let operator works, and when we should use it?

Tuong Le
  • 18,533
  • 11
  • 50
  • 44

1 Answers1

39

&tldr;

It is a convenience function for being able to compartmentalize logic and inject it into a pipeline.

Longer Explanation

The source is probably the most definitive explanation. It is really just passing a function which gets called with a source Observable.

Rx.Observable.prototype.let = function(fn) {
  return fn(this);
}

The utility of this is that we can create or pre-define a pipeline that you want to reuse for multiple sources. Consider a common trope for Rx, the reactive search bar:

// Listen to a key up event on the search bar 
// and emit the value of the search
Rx.Observable.fromEvent(searchBar, 'keyup', e => e.target.value)
  // Don't search too eagerly
  .filter(text => text.length > 3)
  .debounceTime(500)
  //Search logic
  .flatMap(text => $.getJSON(`my/search/api?q=${text}`))
  .flatMap({results} => results)
  //Handler
  .subscribe(appendToList);

The above should give a basic sense of the structure of how a pipeline might be created. If we wanted to try and abstract some of this logic either to clean up the code or to be able to use it elsewhere it can be a little tricky, because it usually means creating a new operator (and that has its own headaches).

The solution is a relatively simple approach of pulling common logic into a function that can be passed a source Observable and will return a new Observable with that logic applied.

So the above might become:

//Defined in pipelines.js
function filterBuilder(minText, debounceTime) {
  return (source) => 
    source.filter(text => text.length > minText)
          .debounce(debounceTime);
}

function queryBuilder(baseUrl) {
  return (source) => 
    source.flatMap(text => $.getJSON(`${baseUrl}?q=${text}`))
          .flatMap({results} => results);
}


//In your application code

Rx.Observable.fromEvent(searchBar, 'keyup', e => e.target.value)
  .let(filterBuilder(3, 500))
  .let(queryBuilder('my/search/api'))
  .subscribe(appendResults);
paulpdaniels
  • 18,395
  • 2
  • 51
  • 55
  • 1
    It sounds like `let` is to `select` as `switchMap` is to `map`. Yet, `select` is `map`, so how is `let` different than `switchMap`? – John Christopher Jones Dec 05 '16 at 23:36
  • 3
    @JohnChristopherJones not really. For one, `select` and `map` are the same function just aliases of each other. `let` is about creating a separation of concerns cleanly, by allowing you to define a pipeline based on some abstract `source` and then plug it into the concrete source. – paulpdaniels Dec 05 '16 at 23:40
  • 1
    Thanks. I've been seeing both `select` and `let` used in examples of the selector pattern in [github.com/ngrx/example-app] and not quite wrapping my head around it. So, in jQuery terms, `let` is literally the method chaining invocation, letting you parameterize sections of method chains. Or, to compare it to `switchMap`, the functions you pass must both return Observables but `switchMap` receives stream members and `let` receives the stream itself. – John Christopher Jones Dec 05 '16 at 23:50
  • 1
    Correct. And actually I may have mis-spoken I didn't realize you were using `ngrx`, I believe `select` with relation to an `ngrx` store relates to the key you are interest in listening. RxJS 4 used the `select` -> `map` alias since it related to microsoft's naming in DotNet. – paulpdaniels Dec 06 '16 at 00:00
  • A nice example indeed – Joseph King Sep 27 '17 at 12:46