mobxjs / mobx

Simple, scalable state management.
http://mobx.js.org
MIT License
27.38k stars 1.76k forks source link

Comparison to other reactive libraries #18

Closed rodryquintero closed 8 years ago

rodryquintero commented 8 years ago

I would like to see a comparison between Mobservable and other reactive libraries.

mweststrate commented 8 years ago

Hi,

That is a hard question since there are many libraries :). Let's first define "(functional) reactive programming":

The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.

  • Heinrich Apfelmus

Generally speaking, I think there are two important flavors of reactive programming:

Event stream based FRP

Event stream based libraries, focus at manipulating streams and are very good at that. Joining, splitting, merging, mapping, sampling etc. These libs are useful if you want to reason about multiple event streams at the same time. Or when throttling plays an important role, like with network traffic. For solving simple problems though, these libraries are quite obtrusive (imho). They need a different mindset. Also, representing every variable in your state as stream might become quite unwieldy. Examples: RxJs and BaconJS.

Transparent FRP

Transparent Reactive programming on the other hand tries to hide reasoning about time. It just applies reactive programming in the background. Like with event based, TFRP updates views whenever needed. The difference is that in TFRP you don't define how and when stuff updates. While with streams you (have to) define that explicitly.

The tracker library of Meteor summarizes this nicely:

Tracker gives you much of the power of a full-blown Functional Reactive Programming (FRP) system without requiring you to rewrite your program as a FRP data flow graph. https://www.meteor.com/tracker

Examples of frameworks that apply TFRP libs are knockoutJS, EmberJS and Meteor. Mobservable is stand-alone, although it ships with a small ReactJS binding.

These are things that makes mobservable distinctable from other TFRP libraries:

  1. Changes are always processed synchronously and atomically; You can never observe intermediate values. This makes reasoning simpler and avoids weird edge case behavior.
  2. Mobservable doesn't ship with its own reactive structures. Instead, all structures behave like normal javascript objects, classes and arrays (although array's will have some caveats until ES7).
  3. Its a stand-alone solution.

I'll hope that answers your question.

rodryquintero commented 8 years ago

Thanks. This answer helps to understand Mobservable better. El El jue, 20 ago 2015 a las 1:53 p.m., Michel Weststrate < notifications@github.com> escribió:

Hi,

That is a hard question since there are many libraries :). Let's first define "(functional) reactive programming":

The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.

  • Heinrich Apfelmus

Generally speaking, I think there are two important flavors of reactive programming: Event stream based FRP

Event stream based libraries, focus at manipulating streams and are very good at that. Joining, splitting, merging, mapping, sampling etc. These libs are useful if you want to reason about multiple event streams at the same time. Or when throttling plays an important role, like with network traffic. For solving simple problems though, these libraries are quite obtrusive (imho). They need a different mindset. Also, representing every variable in your state as stream might become quite unwieldy. Examples: RxJs and BaconJS. Transparent FRP

Transparent Reactive programming on the other hand tries to hide reasoning about time. It just applies reactive programming in the background. Like with event based, TFRP updates views whenever needed. The difference is that in TFRP you don't define how and when stuff updates. While with streams you (have to) define that explicitly.

The tracker library of Meteor summarizes this nicely:

Tracker gives you much of the power of a full-blown Functional Reactive Programming (FRP) system without requiring you to rewrite your program as a FRP data flow graph. https://www.meteor.com/tracker

Examples of frameworks that apply TFRP libs are knockoutJS, EmberJS and Meteor. Mobservable is stand-alone, although it ships with a small ReactJS binding.

These are things that makes mobservable distinctable from other TFRP libraries:

  1. Changes are always processed synchronously and atomically; You can never observe intermediate values. This makes reasoning simpler and avoids weird edge case behavior.
  2. Mobservable doesn't ship with its own reactive structures. Instead, all structures behave like normal javascript objects, classes and arrays (although array's will have some caveats until ES7).
  3. Its a stand-alone solution.

I'll hope that answers your question.

— Reply to this email directly or view it on GitHub https://github.com/mweststrate/mobservable/issues/18#issuecomment-133117581 .

mehdi-cit commented 8 years ago

Would it be possible to give some insight on the benefit of using Mobservable over Tracker? I'm new to both Mobservable and Tracker but from what I gathered around the internet Tracker usage may lead to 'infinite reactive loop'. Is such problem automatically avoidable by Mobservable? (I hope I am not saying something stupid but going through the 5 minutes tutorial, it seems the 'syncing' would only be 'evaluated' if there's a real change in the underlying data)

mweststrate commented 8 years ago

Hi @mehdi-cit ,

The pure basics of libraries are very similar; the dependency of functions will be tracked automatically, so that the can be re-run when one of their dependencies changes. Tracker.autorun(func) is very similar to mobservable.makeReactive(func).

These are the interesting differences however:

Oh it is true indeed that changes are only propagated if a computation really resulted in a new value indeed. But I'm not sure whether the same holds for tracker or not.

P.s.: an atomic update is this:

var thing = makeReactive({
  value: 2,
  squared: () => this.value * this.value,
  cubic: () => this.value * this.squared
});

now suppose you are observing cubic, and altering thing.value from 2 to 3. That change will both update squared and cubic. But squared itself will also trigger an update of cubic. Mobservable makes sure that the update of cubic is atomic; it will transition from 8 directly to 27, and not yield intermediate values like 12 (square 2 * 3) or 18 (square 3 * 2).

mweststrate commented 8 years ago

Oh, another notable difference is that there is hardly any cleanup to do in mobservable, only sideEffects need to be disposed explicitly by the programmer.

mehdi-cit commented 8 years ago

Thank you Sir. The more I read and test the more I like mobservable! I cannot help but imagine how I could use it many different ways (not just UI stuff). I do not want to get ahead of myself but we might have here what could possibly be the greatest js library ever.

rodryquintero commented 8 years ago

Along this lines i would also like to point out that i wish i knew about this library before i started coding for a project i am working on.

Good job!

El El vie, 21 ago 2015 a las 9:47 a.m., mehdi-cit notifications@github.com escribió:

Thank you Sir. The more I read and test the more I like mobservable! I cannot help but imagine how I could use it many different ways (not just UI stuff). I do not want to get ahead of myself but we might have here what could possibly be the greatest js library ever.

— Reply to this email directly or view it on GitHub https://github.com/mweststrate/mobservable/issues/18#issuecomment-133449591 .

aleclarson commented 7 years ago

You can just use plain arrays, objects and classes after passing them through makeReactive.

I assume Object.defineProperty is used to make objects/classes reactive. But how are "plain arrays" made reactive? Via some proxy?

Oh it is true indeed that changes are only propagated if a computation really resulted in a new value indeed. But I'm not sure whether the same holds for tracker or not.

Here's an excerpt from the Meteor docs about their ReactiveVar class:

"An important property of ReactiveVars — which is sometimes a reason for using one — is that setting the value to the same value as before has no effect;"

So if you're using a ReactiveVar, changes are not propagated for duplicate values. But when using Tracker.Dependency, all change events are propagated regardless.

mweststrate commented 7 years ago

@aleclarson observable arrays are basically objects with numeric keys and all the other array methods