kefirjs / kefir

A Reactive Programming library for JavaScript
https://kefirjs.github.io/kefir/
MIT License
1.87k stars 97 forks source link

How do you persist a value from one observable downstream when using merge? #189

Closed indigo0086 closed 8 years ago

indigo0086 commented 8 years ago

My scenario is for a drag and drop implementation. I wanted to mape a value to the drag-start and drag-end observables, but also persist a value from dragstart to dragend. I can't seem to do this without some weird usage of sample by

Kefir.merge([
  dragStart.map((e) => ({elem: e.currentTarget, value: true })),
  dragEnd.map(() => ({value: false}))
]).log()

for this, I will obviously get two seperate values and dragend will not emit the elem. I wanted to solve this issue without keeping a global variable outside the scope so this is the best I could come up with. Not sure how efficient or correct it is, but I wanted to ask if this is the correct solution to my problem or is there another way to do a merge where I can pass data from one observable downstream to be emitted by other observables.

Kefir.merge([
  dragStart.map((e) => ({elem: e.currentTarget, value: true })),
  dragStart.sampledBy(dragEnd, (start, end) => {
    return { elem, start.currentTarget, value: false }
  })
]).log()

Maybe I'm missing something

rpominov commented 8 years ago

merge probably isn't the right tool for this task. I'd probably use flatMap:

dragStart.flatMap(e => {
  return Kefir.merge([
    Kefir.constant({elem: e.currentTarget, value: true}),
    dragEnd.take(1).map(() => ({elem: e.currentTarget, value: false})),
  ])
}).log()
indigo0086 commented 8 years ago

Thanks, works I just need some time to wrap my head around understanding it.

rpominov commented 8 years ago

Yeah, sorry, flatMap is kind of tricky, but once you understand it, it enables whole new category of patterns.

indigo0086 commented 8 years ago

just thinkign about it, is this pretty much what is occurring. I get the constant returns immediately because it's essentially the mapped representation of the dragStart, but not sure about why the dragEnd uses .take(1)

dragStart:  -<elem>X
dragEnd:    -------------------------<elem>x 
flatMapOp:  -<const>-----------------<dragEndMap>x

is this what is happening?

rpominov commented 8 years ago

Yes, looks right. I was't sure what dragEnd is, so I added .take(1) just in case. Looking at your graph it's probably not needed.

The .take(1) would be needed if your source streams looked like this:

dragStart: -e------e------e------e-----
dragEnd:   ----e------e------e------e-----

So for each start event we would like only the one following end event.

indigo0086 commented 8 years ago

Great, starting to get it now. Definitely understanding flatten makes this easier to understand.