4

The rx guidelines say to avoid side effects when possible, and put them in do() (doAction in js) clauses if they are unavoidable.

However, a very common side effect in a UI is to create some resource (say a <div>) that is referenced down-stream (by children widgets). You have to capture the handles for these resources so they can be passed on. E.g. if you have an array of data, each requiring a div, you would create a div for each and pass the handles for these divs to the children.

However doAction() discards the return value of the side effect, so you can't capture the handles of the objects that are created. You have to do the side effect in a select().

Am I looking at this all wrong? Resources that are created are state, and are side effecting. You want the state in the stream, but you can't put it in the stream without putting side effects in select(), which is contraindicated.

user1009908
  • 1,390
  • 13
  • 22

2 Answers2

4

Keep in mind that they are just guidelines. If you want a side effect in you select function and you understand how it will be used then go for it.

But also...have you considered creating detached elements and only attaching them to the document in your subscribe callback? In other words it is not the creation of the resource that is the side effect. It is only when you do something with the resource. I've used this pattern a few times...

$(someElement).onAsObservable("click")
    .select(function(ev) {
            return $("<div>");
     })
     ...do stuff to detached div
     .subscribe(function($el) {
          // finally attach it
          $(container).append($el);
     });
Brandon
  • 38,310
  • 8
  • 82
  • 87
  • To allow passing it to a child widget, I'd need to capture the intermediate stream (before the subscribe). Is the order of execution well-defined in that case? I mean, does the child know that the element is or is not inserted in the DOM? divs = /* the stream of divs */; dispatch_to_children(divs); divs.subscribe(/* insert in DOM */); Do they execute in order? Seems like it could be bad to rely on order unless it's well-defined in rx. – user1009908 Feb 07 '14 at 05:19
  • Is Observable.using() related to this issue? I just found it while searching for "resources". I don't think introtorx covers this. Not sure where to find examples. – user1009908 Feb 07 '14 at 05:24
  • can you post code that represents your use case? what is the child widget doing with the stream of elements? There are so so many variations that it is difficult to provide the correct advice without more information. – Brandon Feb 07 '14 at 12:16
  • I don't have a code example yet, but I know from past experience that some DOM calls that a child widget may need to perform will behave differently on a disconnected element, e.g. things having to do with page layout. In those cases the child will need to know if it has been inserted. – user1009908 Feb 07 '14 at 17:46
  • Well, without a specific case, it is hard to get more specific than "sometimes it is OK to have a side effect in your select methods". – Brandon Feb 08 '14 at 13:06
2

This is a common issue with FRP. I haven't found a better way than to bend the rules a bit. But instead of using select / map, I tend to use selectMany / flatMap, which allows me to return a stream of events directly.

Like below. (The snippet is Bacon.js code but can be easily converted to RxJs)

var allClicks = event.flatMap(function(value) {
  var widget = $("<input>") // etc
  $("form").append(widget)
  return widget.asEventStream("click")
})

So, for each input event, you create a new piece of UI and return a stream of events from that one. The result stream "allClicks" gets all the events from the spawned streams.

raimohanska
  • 3,265
  • 17
  • 28