New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Understanding MobX and when to use it. #199

Open
AriaFallah opened this Issue Apr 18, 2016 · 62 comments

Comments

Projects
None yet
@AriaFallah

AriaFallah commented Apr 18, 2016

Recently having used MobX, I'm trying to reason about why I'm really using it, and trying to truly understand the pros/cons vs redux and cycle.

For redux, I think that it comes down to the fact that most people do not need the number one thing redux has to offer, extreme predicability and extreme testability, because their apps are not complex enough. Thus, when they're writing a bunch of reducers, dealing with extra verbosity, and having trouble grasping the new concepts, only to not reap the benefits, they feel like redux isn't all that useful.

For cycle, I feel like the same way you've written in your docs, most people don't need the complexity and power that RxJS brings to the table over the more simple API MobX provides. MobX also lets you stick to the OOP style that most people are familiar with unlike Cycle, which heavily favors pure composable functions.

Basically MobX lets you write your code as you normally would without forcing you to adopt and learn many new paradigms, and moreover, abstracts away the need to understand your view rendering logic. I think the real power of MobX is the fact that it's just easy.

However, this also makes me wonder.

Redux, ignoring its other limitations and its verbosity, will allow you to write an application that you are familiar with from top to bottom. If you put in the work, it'll be easy to get 100% coverage in tests, and to reason about piece by piece how your program flows.

Cycle despite being more complex, following different paradigms, and needing you to understand RxJS ultimately seems more powerful than MobX if you grasp everything about it.

Do you think the above is accurate?

Also, where do you think MobX fits in when your application grows to be very complex? Like I said above MobX's appeal to me is that it provides similar results to the more complex libraries all while keeping it simple and easy to learn. But when should one pick MobX over Redux or Cycle when they're fully committed to learning and accommodating the complexities of either library? While MobX seems just as capable, the alternatives seem more advantageous if you invest the large amount time necessary to understand them. Is this accurate as well?

An obligatory thank you for writing the library. I'm using it, and enjoying using it. This isn't a critique or anything, but just a deeper dive into understanding its place among other available tools.

@hnordt

This comment has been minimized.

Show comment
Hide comment
@hnordt

hnordt Apr 19, 2016

Contributor

I don't know Cycle and I have no interest learning it now, so I'll give my opinion on Redux vs MobX.

I've been using Redux since it was released. It's very predictable and testable, but takes a lot time to write a full module, because it needs a lot boilerplate.

Of course you can write helpers to help with less boilerplate, but it still needs boilerplate.

I'm sure you can archieve the same predictability and testability with MobX if you implement it the right way. MobX doesn't force you to implement it in a specific way, you have freedom.

You can even use the same structure as you would use with any Redux app: mobxjs/mobx-react-boilerplate#8

I'm using MobX now because I can write code 3x faster than with Redux, but my codebase still is predictable and testable.

In the end of the day Redux and MobX are just concepts. Choose Redux if you want to have full control over dispatching actions and transforming state. Go with MobX if you prefer to don't manually handle action dispatchments and state transformations.

I think that implemented in the right way, MobX is a natural evolution of Redux, you can trust MobX to manage your state, you just tell what you want, instead teaching MobX how to do it.

I think the main difference between Redux and MobX is that for Redux you need to "teach" how to dispatch actions and transform state, for MobX you just trust that MobX will do a good job, you just tell MobX "do it", and MobX does.

(it's just my personal opinion after using both libraries/concepts in production)

Contributor

hnordt commented Apr 19, 2016

I don't know Cycle and I have no interest learning it now, so I'll give my opinion on Redux vs MobX.

I've been using Redux since it was released. It's very predictable and testable, but takes a lot time to write a full module, because it needs a lot boilerplate.

Of course you can write helpers to help with less boilerplate, but it still needs boilerplate.

I'm sure you can archieve the same predictability and testability with MobX if you implement it the right way. MobX doesn't force you to implement it in a specific way, you have freedom.

You can even use the same structure as you would use with any Redux app: mobxjs/mobx-react-boilerplate#8

I'm using MobX now because I can write code 3x faster than with Redux, but my codebase still is predictable and testable.

In the end of the day Redux and MobX are just concepts. Choose Redux if you want to have full control over dispatching actions and transforming state. Go with MobX if you prefer to don't manually handle action dispatchments and state transformations.

I think that implemented in the right way, MobX is a natural evolution of Redux, you can trust MobX to manage your state, you just tell what you want, instead teaching MobX how to do it.

I think the main difference between Redux and MobX is that for Redux you need to "teach" how to dispatch actions and transform state, for MobX you just trust that MobX will do a good job, you just tell MobX "do it", and MobX does.

(it's just my personal opinion after using both libraries/concepts in production)

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah Apr 19, 2016

I've had basically exactly the same experience as you, and I think we've reached the same conclusion.

Although I am curious, how do you test your MobX application?

That's actually what caused me to write this in the first place. I was wondering how testing MobX was different from testing Redux.

Redux is built on the reducers, has time travel and everything is explicit, which is what I think is the only true advantage it has over MobX. This is also it's disadvantage as well interestingly.

So given all that how do you match the predictability of Redux with MobX in your testing?

AriaFallah commented Apr 19, 2016

I've had basically exactly the same experience as you, and I think we've reached the same conclusion.

Although I am curious, how do you test your MobX application?

That's actually what caused me to write this in the first place. I was wondering how testing MobX was different from testing Redux.

Redux is built on the reducers, has time travel and everything is explicit, which is what I think is the only true advantage it has over MobX. This is also it's disadvantage as well interestingly.

So given all that how do you match the predictability of Redux with MobX in your testing?

@hnordt

This comment has been minimized.

Show comment
Hide comment
@hnordt

hnordt Apr 19, 2016

Contributor

@AriaFallah I'm not an expert with testing, but just try to create your functions as pure as possible, for example:

class MessageStore {
  // bad
  markMessageAsRead = message => {
    if (message.status === 'new') {
      fetch({
        method: 'GET',
        path: `/notification/read/${message.id}`
      }).then(() => message.status = 'read')
    }
  }
  // good
  markMessageAsRead = message => {
    if (message.status !== 'new') {
      return Promise.reject('Message is not new')
    }
    // it's now easily mockable
    return api.markMessageAsRead(message).then(() => {
      // this is a pure function
      // you can test it easily
      return this.updateMessage(message, { status: ' read' })
    })
  }
}

Redux is just javascript, just follow some of the concepts, for example, actions in Redux are the same as MobX, the only difference is the state transformation, for transforming state, create pure functions and pass data around.

Contributor

hnordt commented Apr 19, 2016

@AriaFallah I'm not an expert with testing, but just try to create your functions as pure as possible, for example:

class MessageStore {
  // bad
  markMessageAsRead = message => {
    if (message.status === 'new') {
      fetch({
        method: 'GET',
        path: `/notification/read/${message.id}`
      }).then(() => message.status = 'read')
    }
  }
  // good
  markMessageAsRead = message => {
    if (message.status !== 'new') {
      return Promise.reject('Message is not new')
    }
    // it's now easily mockable
    return api.markMessageAsRead(message).then(() => {
      // this is a pure function
      // you can test it easily
      return this.updateMessage(message, { status: ' read' })
    })
  }
}

Redux is just javascript, just follow some of the concepts, for example, actions in Redux are the same as MobX, the only difference is the state transformation, for transforming state, create pure functions and pass data around.

@amsb

This comment has been minimized.

Show comment
Hide comment
@amsb

amsb Apr 19, 2016

I've enjoyed the aromas emanating from the melting pot of ideas in JavaScript front-end development, but with such a smorgasbord it can be hard to decide what to eat. Redux is built on solid theoretical foundations with a simple essence and a growing community of experienced developers that makes it an extremely compelling candidate for many projects.

That said, my foray into using it left me with a project that felt disjoint in its organization. In particular, I felt this way when coming back to my small project after an absence and needed to mentally trace the thread of logic for an asynchronous action through multiple functions and files. I fully admit that this experience probably reflects my deficiencies more than that of the tools, but nevertheless I was curious to explore a different balance which led me to MobX and my mobx-reactor experiment.

In many ways, my experiment effectively replaces the suite of redux+redux-saga+immutablejs+reselect with MobX (and my library) in a way that is perhaps appropriate for some projects due to their size/scale/velocity/etc. What I learned in doing this is that I ultimately exchanged explicitness (i.e. verbosity/boilerplate) and disjointness (a positive in the context of testability and a negative in the context of organization) with tight organization and bit of implicit "magic" (via MobX managing updates through observables).

One of the things I really appreciate about the Redux approach is the single stream of application events that occur in the form of actions dispatched through a single application store and processed by middleware which furnishes similar opportunities as available during request/response processing of traditional application servers.

amsb commented Apr 19, 2016

I've enjoyed the aromas emanating from the melting pot of ideas in JavaScript front-end development, but with such a smorgasbord it can be hard to decide what to eat. Redux is built on solid theoretical foundations with a simple essence and a growing community of experienced developers that makes it an extremely compelling candidate for many projects.

That said, my foray into using it left me with a project that felt disjoint in its organization. In particular, I felt this way when coming back to my small project after an absence and needed to mentally trace the thread of logic for an asynchronous action through multiple functions and files. I fully admit that this experience probably reflects my deficiencies more than that of the tools, but nevertheless I was curious to explore a different balance which led me to MobX and my mobx-reactor experiment.

In many ways, my experiment effectively replaces the suite of redux+redux-saga+immutablejs+reselect with MobX (and my library) in a way that is perhaps appropriate for some projects due to their size/scale/velocity/etc. What I learned in doing this is that I ultimately exchanged explicitness (i.e. verbosity/boilerplate) and disjointness (a positive in the context of testability and a negative in the context of organization) with tight organization and bit of implicit "magic" (via MobX managing updates through observables).

One of the things I really appreciate about the Redux approach is the single stream of application events that occur in the form of actions dispatched through a single application store and processed by middleware which furnishes similar opportunities as available during request/response processing of traditional application servers.

@capaj

This comment has been minimized.

Show comment
Hide comment
@capaj

capaj Apr 20, 2016

Contributor

@AriaFallah

Redux, ignoring its other limitations and its verbosity, will allow you to write an application that you are familiar with from top to bottom. If you put in the work, it'll be easy to get 100% coverage in tests, and to reason about piece by piece how your program flows.

You write it like this is a quality that only Redux has. I have to disagree. There is no obstacle at getting 100% coverage for mobX powered app. In fact it is easier to achieve since the amount of code is smaller than with Redux.

Contributor

capaj commented Apr 20, 2016

@AriaFallah

Redux, ignoring its other limitations and its verbosity, will allow you to write an application that you are familiar with from top to bottom. If you put in the work, it'll be easy to get 100% coverage in tests, and to reason about piece by piece how your program flows.

You write it like this is a quality that only Redux has. I have to disagree. There is no obstacle at getting 100% coverage for mobX powered app. In fact it is easier to achieve since the amount of code is smaller than with Redux.

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah Apr 20, 2016

@capaj Ah okay that's good to know.

Like I said above, I haven't done much testing with MobX. I assumed that Redux, where you have to write everything out explicitly, would be easier to test than MobX because there's less magic, but, as you point out, perhaps MobX is easier to test because that magic helps eliminate a lot of boilerplate that made sense anyways so you need to test fewer parts of your code.

Regardless, the whole point of the post is to get perspectives like yours. I'm not trying to peddle everything in the main post as fact like one would in a medium post. I created it as a result of curiosity and confusion about the concepts of MobX and how it stacks up against the other more popular libraries.

AriaFallah commented Apr 20, 2016

@capaj Ah okay that's good to know.

Like I said above, I haven't done much testing with MobX. I assumed that Redux, where you have to write everything out explicitly, would be easier to test than MobX because there's less magic, but, as you point out, perhaps MobX is easier to test because that magic helps eliminate a lot of boilerplate that made sense anyways so you need to test fewer parts of your code.

Regardless, the whole point of the post is to get perspectives like yours. I'm not trying to peddle everything in the main post as fact like one would in a medium post. I created it as a result of curiosity and confusion about the concepts of MobX and how it stacks up against the other more popular libraries.

@PhiLhoSoft

This comment has been minimized.

Show comment
Hide comment
@PhiLhoSoft

PhiLhoSoft Apr 20, 2016

Contributor

Some input:

  • I know a bit Redux, as I wanted to introduce it to our project. I actually rewrote it in ES5, which made me look into its code (not so big) and to understand how it works. I like the ideas, well explained by Dan Abramov. I finally dropped the idea, partly because of boilerplate, mostly because using a single store of immutable data would be too foreign for my co-workers (and perhaps even for me! I appreciate the ideas, but I have little experience with these fields).
  • I use RxJS in our application, although I can't say I totally master it... Vast API surface! Indeed, as @mweststrate told in a reply (in HN or similar, I can't recall), you can do things similar to MobX in RxJS, but it is a bit convoluted... And as said in the docs, you can freely mix and match both libraries, one for time-driven streams (managing events, etc.) the other for state / data management.
  • I introduced successfully MobX in our application: it adds little boilerplate and feels natural to use. Works well in ES5. What is nice is that it doesn't ask you to structure the application around it! I used it for a specific usage: managing state in each controller (AngularJS) to handle various conditions to disable controls (toolbar buttons, dialog buttons, others) depending on various factors (user input, state of data, waiting for data, etc.).

Testing was not a problem (unlike RxJS parts!): I just have variables, I verify they have the right state on given conditions. That's all.
OK, it changes some things: instead of doing ctrl.observedVar = true for a quick test, I have to set up the variables on which observedVar depends to do the test. Logical.
Also, Jasmine is a bit lost with special MobX objects: its isEqual works well with them, but if the values differ, it reports something like "got { name: (getter) } instead of { name: "foo" }", needing a bit more work to see what went wrong.

Overall, experience with MobX is very good.

Contributor

PhiLhoSoft commented Apr 20, 2016

Some input:

  • I know a bit Redux, as I wanted to introduce it to our project. I actually rewrote it in ES5, which made me look into its code (not so big) and to understand how it works. I like the ideas, well explained by Dan Abramov. I finally dropped the idea, partly because of boilerplate, mostly because using a single store of immutable data would be too foreign for my co-workers (and perhaps even for me! I appreciate the ideas, but I have little experience with these fields).
  • I use RxJS in our application, although I can't say I totally master it... Vast API surface! Indeed, as @mweststrate told in a reply (in HN or similar, I can't recall), you can do things similar to MobX in RxJS, but it is a bit convoluted... And as said in the docs, you can freely mix and match both libraries, one for time-driven streams (managing events, etc.) the other for state / data management.
  • I introduced successfully MobX in our application: it adds little boilerplate and feels natural to use. Works well in ES5. What is nice is that it doesn't ask you to structure the application around it! I used it for a specific usage: managing state in each controller (AngularJS) to handle various conditions to disable controls (toolbar buttons, dialog buttons, others) depending on various factors (user input, state of data, waiting for data, etc.).

Testing was not a problem (unlike RxJS parts!): I just have variables, I verify they have the right state on given conditions. That's all.
OK, it changes some things: instead of doing ctrl.observedVar = true for a quick test, I have to set up the variables on which observedVar depends to do the test. Logical.
Also, Jasmine is a bit lost with special MobX objects: its isEqual works well with them, but if the values differ, it reports something like "got { name: (getter) } instead of { name: "foo" }", needing a bit more work to see what went wrong.

Overall, experience with MobX is very good.

@hnordt

This comment has been minimized.

Show comment
Hide comment
@hnordt

hnordt Apr 21, 2016

Contributor

@capaj @PhiLhoSoft

Would be awesome if you share some of your testing approaches with MobX.

Contributor

hnordt commented Apr 21, 2016

@capaj @PhiLhoSoft

Would be awesome if you share some of your testing approaches with MobX.

@hellectronic

This comment has been minimized.

Show comment
Hide comment
@hellectronic

hellectronic Apr 23, 2016

Contributor

@AriaFallah @hnordt Do you have concrete questions in regard to testing?

Contributor

hellectronic commented Apr 23, 2016

@AriaFallah @hnordt Do you have concrete questions in regard to testing?

@capaj

This comment has been minimized.

Show comment
Hide comment
@capaj

capaj Apr 23, 2016

Contributor

@hnordt I have an article on mobx recipes in the making. Will include some samples and showcases on testing. Give me 1-3 weeks, I'll post it then.

Contributor

capaj commented Apr 23, 2016

@hnordt I have an article on mobx recipes in the making. Will include some samples and showcases on testing. Give me 1-3 weeks, I'll post it then.

@PhiLhoSoft

This comment has been minimized.

Show comment
Hide comment
@PhiLhoSoft

PhiLhoSoft Apr 23, 2016

Contributor

Well, as I wrote, testing wasn't much specific. At least in my use case. I added MobX in a very local way (controller level) in an existing AngularJS 1 application, to replace part of the code.
Not using it as a central store, as it would have been too disruptive. We have already some unit tests (made with Jasmine, run with Karma, classical in the Angular world), not enough because of deadlines and so.
But well, the updates I had to do to accommodate the introduction of MobX were minor, as explained above. That's the beauty of MobX: it is quite transparent... And not opinionated, so we can use it outside of React, in a non pervasive way, my way and not in the way envisioned by the project.

Contributor

PhiLhoSoft commented Apr 23, 2016

Well, as I wrote, testing wasn't much specific. At least in my use case. I added MobX in a very local way (controller level) in an existing AngularJS 1 application, to replace part of the code.
Not using it as a central store, as it would have been too disruptive. We have already some unit tests (made with Jasmine, run with Karma, classical in the Angular world), not enough because of deadlines and so.
But well, the updates I had to do to accommodate the introduction of MobX were minor, as explained above. That's the beauty of MobX: it is quite transparent... And not opinionated, so we can use it outside of React, in a non pervasive way, my way and not in the way envisioned by the project.

@Keats

This comment has been minimized.

Show comment
Hide comment
@Keats

Keats commented Apr 27, 2016

@AriaFallah posted a short video to some info: https://www.youtube.com/watch?v=83v8cdvGfeA

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah Apr 27, 2016

@Keats haha yeah, but I figured that since I was just summarizing the information here and not as much in depth I wouldn't promote it myself. I appreciate you posting it though 😄 .

I have to thank @hnordt and @capaj for providing me with a lot of the insight I had in this thread to be able to make the video.

AriaFallah commented Apr 27, 2016

@Keats haha yeah, but I figured that since I was just summarizing the information here and not as much in depth I wouldn't promote it myself. I appreciate you posting it though 😄 .

I have to thank @hnordt and @capaj for providing me with a lot of the insight I had in this thread to be able to make the video.

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

IMO the most fundamental difference between Redux and MobX, from a conceptual POV, relates to the update logic.

In Redux, a Reducer encapsulates all the possible ways in which a piece of state can be updated. i.e. you can't (directly) update that piece of state from outside. And the overall state/reducers is organized around this notion of update logic.

In MobX, the state is managed inside observables, but observables act like free slots which accept data from the outside. So in order to tell how the state held by an observable is updated you need to look to all the actions that update that observable.

Typically some (maybe all, depending on the case) of the update logic can be inside a domain class, making observables private and exposing only getters for observable values and the set of actions that update the private observables. But still the update logic will be spread across multiple actions.

So depending on the case: either Redux or MobX will feel easier. I don't think it's related to the size of an application but more to how complex is the update logic. if a domain class can encapsulate all its update logic, and if the overall behavior of the class can be easy to reason about, then effectively MobX will feel easier than Redux (talking about the models not specific implementations).

However if the update logic of the app is such that it can't be encapsulated inside the domain class (e.g. you can't call the class method directly from you UI callback, or the called action will lead to cascaded calls to other actions in other domain classes), then Redux model will feel more suitable here.

yelouafi commented May 23, 2016

IMO the most fundamental difference between Redux and MobX, from a conceptual POV, relates to the update logic.

In Redux, a Reducer encapsulates all the possible ways in which a piece of state can be updated. i.e. you can't (directly) update that piece of state from outside. And the overall state/reducers is organized around this notion of update logic.

In MobX, the state is managed inside observables, but observables act like free slots which accept data from the outside. So in order to tell how the state held by an observable is updated you need to look to all the actions that update that observable.

Typically some (maybe all, depending on the case) of the update logic can be inside a domain class, making observables private and exposing only getters for observable values and the set of actions that update the private observables. But still the update logic will be spread across multiple actions.

So depending on the case: either Redux or MobX will feel easier. I don't think it's related to the size of an application but more to how complex is the update logic. if a domain class can encapsulate all its update logic, and if the overall behavior of the class can be easy to reason about, then effectively MobX will feel easier than Redux (talking about the models not specific implementations).

However if the update logic of the app is such that it can't be encapsulated inside the domain class (e.g. you can't call the class method directly from you UI callback, or the called action will lead to cascaded calls to other actions in other domain classes), then Redux model will feel more suitable here.

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah May 23, 2016

@yelouafi

I've never thought about it that way 😮

I do have one question though. When you say:

However if the update logic of the app is such that it can't be encapsulated inside the domain class (e.g. you can't call the class method directly from you UI callback, or the called action will lead to cascaded calls to other actions in other domain classes), then Redux model will feel more suitable here.

Could you elaborate on what you mean by can't call the class method directly from your UI callback, and also why it's easier to use Redux in this case vs MobX? I'm having trouble visualizing what you're saying. Is it that MobX can't wrap all of the update logic in a single place like Redux reducers can?

AriaFallah commented May 23, 2016

@yelouafi

I've never thought about it that way 😮

I do have one question though. When you say:

However if the update logic of the app is such that it can't be encapsulated inside the domain class (e.g. you can't call the class method directly from you UI callback, or the called action will lead to cascaded calls to other actions in other domain classes), then Redux model will feel more suitable here.

Could you elaborate on what you mean by can't call the class method directly from your UI callback, and also why it's easier to use Redux in this case vs MobX? I'm having trouble visualizing what you're saying. Is it that MobX can't wrap all of the update logic in a single place like Redux reducers can?

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

Is it that MobX can't wrap all of the update logic in a single place like Redux reducers can?

Say for example, you dispatch an action TODO_ADDED and you have 2 reducers : todoList (to add the new todo to some list) )and onBoarding (for ex. to track user progress) which both react to the same action. In the Todo UI, you'll only do dispatch(todoAdded(...)), the store will dispatch the action to all reducers and both reducers will perform the update logic internally.

Now with mobX, imagine you have 2 classes TodoList and OnBoarding which also both watch for todo creations but each has a different update logic. In this case, you'll have to trigger 2 actions from the UI, one for each class, but then you'll have a part of your update logic in the UI. You may also embed the call to OnBoarding inside the TodoList.addTodo(...) method. Or make a supervisor class which encapsulates the whole logic, and then call the supervisor class from the UI but then you'll have those cascaded actions (e.g. you aren't calling OnBoarding.todoAdded(...) directly from the UI)

One may argue that OnBoarding could be made a reaction to TodoList, but what I'm emphasizing here is that we're not interested in reacting to state changes but to the event itself (e.g. we may want to watch for 3 consecutive TODO_ADDED events)

yelouafi commented May 23, 2016

Is it that MobX can't wrap all of the update logic in a single place like Redux reducers can?

Say for example, you dispatch an action TODO_ADDED and you have 2 reducers : todoList (to add the new todo to some list) )and onBoarding (for ex. to track user progress) which both react to the same action. In the Todo UI, you'll only do dispatch(todoAdded(...)), the store will dispatch the action to all reducers and both reducers will perform the update logic internally.

Now with mobX, imagine you have 2 classes TodoList and OnBoarding which also both watch for todo creations but each has a different update logic. In this case, you'll have to trigger 2 actions from the UI, one for each class, but then you'll have a part of your update logic in the UI. You may also embed the call to OnBoarding inside the TodoList.addTodo(...) method. Or make a supervisor class which encapsulates the whole logic, and then call the supervisor class from the UI but then you'll have those cascaded actions (e.g. you aren't calling OnBoarding.todoAdded(...) directly from the UI)

One may argue that OnBoarding could be made a reaction to TodoList, but what I'm emphasizing here is that we're not interested in reacting to state changes but to the event itself (e.g. we may want to watch for 3 consecutive TODO_ADDED events)

@glenjamin

This comment has been minimized.

Show comment
Hide comment
@glenjamin

glenjamin May 23, 2016

Is it that MobX can't wrap all of the update logic in a single place like Redux reducers can?

I think this is the fundamental difference, most of the rest is just implementation details.

Redux (and flux in general) forces you to write your data updates outside the components at the top of your application, and enumerate all possible updates.

MobX doesn't enforce this, however there's nothing that prevents you from doing it this way.

glenjamin commented May 23, 2016

Is it that MobX can't wrap all of the update logic in a single place like Redux reducers can?

I think this is the fundamental difference, most of the rest is just implementation details.

Redux (and flux in general) forces you to write your data updates outside the components at the top of your application, and enumerate all possible updates.

MobX doesn't enforce this, however there's nothing that prevents you from doing it this way.

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah May 23, 2016

@yelouafi

I see. So you're saying that you can't mutate two different domains, Todos and OnBoarding for example, at the same time without using something like computed, which isn't reacting to the event itself, but to the mutation of another observable.

My question would be, couldn't you solve the problem by taking a "redux-like" approach and having a single store at the root that accepts these events?

AriaFallah commented May 23, 2016

@yelouafi

I see. So you're saying that you can't mutate two different domains, Todos and OnBoarding for example, at the same time without using something like computed, which isn't reacting to the event itself, but to the mutation of another observable.

My question would be, couldn't you solve the problem by taking a "redux-like" approach and having a single store at the root that accepts these events?

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

[Update] @AriaFallah this may answer your last question
I want to add that the 2 approaches are not mutually exclusive: we can have advantage from the 2 words by combining the pros of each approach

  • From Redux the reducer being able to encapsulate all the update logic and not having those 'free slots' (observables)
  • From MobX with the dynamic and self-adjusting dependency model (automatic dep. tarcking) which offers a great way to express reactive computations and also finer grained observation

So what could be the combination Reducer + Observable?

IMO, the question has already been ansewred a long time ago by FRP. I'm not talking about RxJS here because Rx has only one half of FRP: the discrete part which is event streams. The other half is the continuous part known as Behaviors (cf. original paper of Conal elliott on FRP)

A Behavior models also a time varying value. But unlike discrete streams, a behavior has always a value (even from the start). here you can view it like an observable but which can not be updated arbitrarily. When you declare a behavior you must declare its update logic at the declaration. And the update logic can be specified with 2 things: the Event streams which affects the behavior state and the reducer which will handle the event streams)

Here is a simple example of the todos example (I've made this example from a rough sketch but I think the concept could benefit from being implemented in a well tested lib like MobX)

// toggle$, toggleAll$, ... are event streams
function newTodo(id, title) {
  return {
    id, title,
    done: behavior(
      false, // start value
      [ toggle$.filter(eqF(id))   , done => !done ], // reducer for toggle events
      [ toggleAll$                     , (_, done) => done  ] // reducer for toggleAll events
    )
  }
}

export const todoList = behavior(
  [], // initial state
  [
    addTodo$,
    (todos, id) => todos.concat(newTodo(id, editingTodo.value))
  ],
  [ 
    removeTodo$,
    (todos, id) => todos.filter(t => t.id !== id)
  ]
)

// computed property
const allDone = computed(() => todoList().length && todoList().every(t => t.done()))

Like in Redux, you can trigger an update from an event stream and it'll update all the behaviors depending on that event

yelouafi commented May 23, 2016

[Update] @AriaFallah this may answer your last question
I want to add that the 2 approaches are not mutually exclusive: we can have advantage from the 2 words by combining the pros of each approach

  • From Redux the reducer being able to encapsulate all the update logic and not having those 'free slots' (observables)
  • From MobX with the dynamic and self-adjusting dependency model (automatic dep. tarcking) which offers a great way to express reactive computations and also finer grained observation

So what could be the combination Reducer + Observable?

IMO, the question has already been ansewred a long time ago by FRP. I'm not talking about RxJS here because Rx has only one half of FRP: the discrete part which is event streams. The other half is the continuous part known as Behaviors (cf. original paper of Conal elliott on FRP)

A Behavior models also a time varying value. But unlike discrete streams, a behavior has always a value (even from the start). here you can view it like an observable but which can not be updated arbitrarily. When you declare a behavior you must declare its update logic at the declaration. And the update logic can be specified with 2 things: the Event streams which affects the behavior state and the reducer which will handle the event streams)

Here is a simple example of the todos example (I've made this example from a rough sketch but I think the concept could benefit from being implemented in a well tested lib like MobX)

// toggle$, toggleAll$, ... are event streams
function newTodo(id, title) {
  return {
    id, title,
    done: behavior(
      false, // start value
      [ toggle$.filter(eqF(id))   , done => !done ], // reducer for toggle events
      [ toggleAll$                     , (_, done) => done  ] // reducer for toggleAll events
    )
  }
}

export const todoList = behavior(
  [], // initial state
  [
    addTodo$,
    (todos, id) => todos.concat(newTodo(id, editingTodo.value))
  ],
  [ 
    removeTodo$,
    (todos, id) => todos.filter(t => t.id !== id)
  ]
)

// computed property
const allDone = computed(() => todoList().length && todoList().every(t => t.done()))

Like in Redux, you can trigger an update from an event stream and it'll update all the behaviors depending on that event

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

And of course the other way is also possible. You can embed behaviors in Redux (with some restrictions thou to make serializability/hot reload possible) and implement an automatic dep. model (like observable selectors) on top of that

yelouafi commented May 23, 2016

And of course the other way is also possible. You can embed behaviors in Redux (with some restrictions thou to make serializability/hot reload possible) and implement an automatic dep. model (like observable selectors) on top of that

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah May 23, 2016

@yelouafi

I actually was looking into something similar recently while looking into FRP. I was experimenting with combining MobX and Most.js to get a mix of the event and behavior/cell streams, but didn't get very far.

I guess my question at this point is that if FRP with both event and behavior streams is the solution, how come it hasn't been created/used yet? Are there any drawbacks?

AriaFallah commented May 23, 2016

@yelouafi

I actually was looking into something similar recently while looking into FRP. I was experimenting with combining MobX and Most.js to get a mix of the event and behavior/cell streams, but didn't get very far.

I guess my question at this point is that if FRP with both event and behavior streams is the solution, how come it hasn't been created/used yet? Are there any drawbacks?

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

Cant say this is THE solution, this is just my POV. Many will find no issues on writing code with free observables b/c it maps directly to their mental model. Others will prefer FRP style updates. And domain space can also make either option more appealing

That being said, and although I didnt looked much into different libs I think Bacon.js has a similar concept called Property and also flyd streams may take an initial value.

But AFAIK there is no lib which combines the dynamic dep. model of Mobx/Ko with FRP reactive behaviors.

For example, in Bacon you cant access to the value of a property directly using prop() but you'll have to snapshot it with some event stream.

yelouafi commented May 23, 2016

Cant say this is THE solution, this is just my POV. Many will find no issues on writing code with free observables b/c it maps directly to their mental model. Others will prefer FRP style updates. And domain space can also make either option more appealing

That being said, and although I didnt looked much into different libs I think Bacon.js has a similar concept called Property and also flyd streams may take an initial value.

But AFAIK there is no lib which combines the dynamic dep. model of Mobx/Ko with FRP reactive behaviors.

For example, in Bacon you cant access to the value of a property directly using prop() but you'll have to snapshot it with some event stream.

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

And I'd just like to add that, in my POV, there is an added value on putting as much as you can of your logic into the 'pure side'. The world of functions is 'eternal': a relation between 2 things is like an invariant captured in your program, insensible to time, and wont be affected by how things get sequenced on the external world (i mean the relation). You can view it like eager (non lazy) mobx derivations which ensure consistency w relation to the event world

yelouafi commented May 23, 2016

And I'd just like to add that, in my POV, there is an added value on putting as much as you can of your logic into the 'pure side'. The world of functions is 'eternal': a relation between 2 things is like an invariant captured in your program, insensible to time, and wont be affected by how things get sequenced on the external world (i mean the relation). You can view it like eager (non lazy) mobx derivations which ensure consistency w relation to the event world

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah May 23, 2016

Forgive me if I'm asking too many questions, but I have a few more.

There is an added value on putting as much as you can of your logic into the 'pure side'

  • When you mention purity, at least in relation to MobX, I think of its emphasis on mutation. Do you think there are any benefits MobX can gain from being more pure/immutable? When I think of immutability, I think of thread safety, which doesn't apply to JS, referential integrity, which MobX already gets through observability, and shallow comparing, which I don't think is all that great. Is there something I'm not thinking about...maybe in a big picture sense?

The world of functions is 'eternal': a relation between 2 things is like an invariant captured in your program, insensible to time, and wont be affected by how things get sequenced on the external world (i mean the relation). You can view it like eager (non lazy) mobx derivations which ensure consistency w relation to the event world.

  • I feel like this answers my question above in a sense, but I don't really have a concrete understanding of what you're trying to convey. I get that you're saying because pure functions are deterministic and referentially transparent, they capture an invariant relationship between their parameters that doesn't depend on anything except their values, but how does that relate to mobx derivations and the big picture in general?

AriaFallah commented May 23, 2016

Forgive me if I'm asking too many questions, but I have a few more.

There is an added value on putting as much as you can of your logic into the 'pure side'

  • When you mention purity, at least in relation to MobX, I think of its emphasis on mutation. Do you think there are any benefits MobX can gain from being more pure/immutable? When I think of immutability, I think of thread safety, which doesn't apply to JS, referential integrity, which MobX already gets through observability, and shallow comparing, which I don't think is all that great. Is there something I'm not thinking about...maybe in a big picture sense?

The world of functions is 'eternal': a relation between 2 things is like an invariant captured in your program, insensible to time, and wont be affected by how things get sequenced on the external world (i mean the relation). You can view it like eager (non lazy) mobx derivations which ensure consistency w relation to the event world.

  • I feel like this answers my question above in a sense, but I don't really have a concrete understanding of what you're trying to convey. I get that you're saying because pure functions are deterministic and referentially transparent, they capture an invariant relationship between their parameters that doesn't depend on anything except their values, but how does that relate to mobx derivations and the big picture in general?
@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate May 23, 2016

Member

Don't want to interfere to much in this thread, because it is way more interesting to discover how people perceive MobX than having me talking about how I intended MobX ;-).

But the cool thing that @yelouafi is onto here is probably that the behavior objects are observable, trackable and seemingly mutable to the outside world. However to mutate an object, you have to invoke one if its actions which is still a pure function, thereby easily testable, snapshottable and all advantages that come from that. So it moves the whole tracking / observable thing closer to the FP ánd event streams world, but without giving up on referential consistency etc yet (or gaining the general advantages of immutables) (if I see it correctly).

I think indeed this pattern could be built on top of MobX quite easily. Especially with the intercept api in 2.2 with which you can control where and when objects can be mutated and make them immutable for the outside world.

Member

mweststrate commented May 23, 2016

Don't want to interfere to much in this thread, because it is way more interesting to discover how people perceive MobX than having me talking about how I intended MobX ;-).

But the cool thing that @yelouafi is onto here is probably that the behavior objects are observable, trackable and seemingly mutable to the outside world. However to mutate an object, you have to invoke one if its actions which is still a pure function, thereby easily testable, snapshottable and all advantages that come from that. So it moves the whole tracking / observable thing closer to the FP ánd event streams world, but without giving up on referential consistency etc yet (or gaining the general advantages of immutables) (if I see it correctly).

I think indeed this pattern could be built on top of MobX quite easily. Especially with the intercept api in 2.2 with which you can control where and when objects can be mutated and make them immutable for the outside world.

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 23, 2016

@AriaFallah simply put you can write a program in terms of relation input-output and have the underlying runtime ensure the relation always hold. Actually mobx ensures ref. transparency between obs. and derivations but the part the goes from the event to the obs. mutation is outside of its scope.

I dont emphasize on immutability at this level. In fact the whole purpose of immutabiliy in FP languages is that you cant have ref. Transparency with mutable values. At the 'FRP level' if I can ensure my relations are maintained (ex an observable array propagates change while the underlying raw array us mutated) I'll be fine with it. My goal is not immutability but ref. Transparency.

@mweststrate that would be interesting. If we can ensure trandactional semantics for behavior updates (a root event updates the state in a single transaction like Redux have actually) then I think mobx would make a great complement to actual discrete event stream libs

yelouafi commented May 23, 2016

@AriaFallah simply put you can write a program in terms of relation input-output and have the underlying runtime ensure the relation always hold. Actually mobx ensures ref. transparency between obs. and derivations but the part the goes from the event to the obs. mutation is outside of its scope.

I dont emphasize on immutability at this level. In fact the whole purpose of immutabiliy in FP languages is that you cant have ref. Transparency with mutable values. At the 'FRP level' if I can ensure my relations are maintained (ex an observable array propagates change while the underlying raw array us mutated) I'll be fine with it. My goal is not immutability but ref. Transparency.

@mweststrate that would be interesting. If we can ensure trandactional semantics for behavior updates (a root event updates the state in a single transaction like Redux have actually) then I think mobx would make a great complement to actual discrete event stream libs

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate May 24, 2016

Member

see transaction?

Op di 24 mei 2016 00:37 schreef Yassine Elouafi notifications@github.com:

@AriaFallah https://github.com/AriaFallah simply put you can write a
program in terms of relation input-output and have the underlying runtime
ensure the relation always hold. Actually mobx ensures ref.
transparency between obs. and derivations but the part the goes from the
event to the obs. mutation is outside of its scope.

I dont emphasize on immutability at this level. In fact the whole purpose
of immutabiliy in FP languages is that you cant have ref. Transparency with
mutable values. At the 'FRP level' if I can ensure my relations are
maintained (ex an observable array propagates change while the underlying
raw array us mutated) I'll be fine with it. My goal is not immutability but
ref. Transparency.

@mweststrate https://github.com/mweststrate that would be interesting.
If we can ensure trandactional semantics for behavior updates (a root event
updates the state in a single transaction like Redux have actually) then I
think mobx would make a great complement to actual discrete event stream
libs


You are receiving this because you were mentioned.

Reply to this email directly or view it on GitHub
#199 (comment)

Member

mweststrate commented May 24, 2016

see transaction?

Op di 24 mei 2016 00:37 schreef Yassine Elouafi notifications@github.com:

@AriaFallah https://github.com/AriaFallah simply put you can write a
program in terms of relation input-output and have the underlying runtime
ensure the relation always hold. Actually mobx ensures ref.
transparency between obs. and derivations but the part the goes from the
event to the obs. mutation is outside of its scope.

I dont emphasize on immutability at this level. In fact the whole purpose
of immutabiliy in FP languages is that you cant have ref. Transparency with
mutable values. At the 'FRP level' if I can ensure my relations are
maintained (ex an observable array propagates change while the underlying
raw array us mutated) I'll be fine with it. My goal is not immutability but
ref. Transparency.

@mweststrate https://github.com/mweststrate that would be interesting.
If we can ensure trandactional semantics for behavior updates (a root event
updates the state in a single transaction like Redux have actually) then I
think mobx would make a great complement to actual discrete event stream
libs


You are receiving this because you were mentioned.

Reply to this email directly or view it on GitHub
#199 (comment)

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate May 24, 2016

Member

@yelouafi didn't test it, but an implementation of behavior with mobx@2.2.0-beta.1 would look like this I think:

import {observable, action, asReference} from "mobx"

function behavior(initialState, ...actions) {
    // store state in an observable
    // we use asReference for a one-size-fits-all approach; treat any value as a single atom
    // not using asReference would be slightly efficienter for tracking (e.g. individual properties can be tracked)
    // but would need to deviate per type (e.g. use array.replace, map.merge or extendObservable to merge a new state into the current one)
    const state = observable(asReference(initialState))

    // make sure only our reducers can modify state
    let isRunningReducer = false
    state.intercept(change => {
        if (isRunningReducer)
            return change // OK
        else
            throw new Error("State should be modified by emitting an event")
    })

    state.observe((newValue, oldValue) => {
        // do cool stuff for time travelling, debug tools or similar
    })

    // subscribe to the streams, actions guarantee transaction semantics
    actions.forEach(
        ([stream, reducer]) => 
            stream.subscribe(action(event => {
                isRunningReducer = true
                state.set(reducer(state.get(), event))
                isRunningReducer = false
            }))
    )

    return () => state.get()
}
Member

mweststrate commented May 24, 2016

@yelouafi didn't test it, but an implementation of behavior with mobx@2.2.0-beta.1 would look like this I think:

import {observable, action, asReference} from "mobx"

function behavior(initialState, ...actions) {
    // store state in an observable
    // we use asReference for a one-size-fits-all approach; treat any value as a single atom
    // not using asReference would be slightly efficienter for tracking (e.g. individual properties can be tracked)
    // but would need to deviate per type (e.g. use array.replace, map.merge or extendObservable to merge a new state into the current one)
    const state = observable(asReference(initialState))

    // make sure only our reducers can modify state
    let isRunningReducer = false
    state.intercept(change => {
        if (isRunningReducer)
            return change // OK
        else
            throw new Error("State should be modified by emitting an event")
    })

    state.observe((newValue, oldValue) => {
        // do cool stuff for time travelling, debug tools or similar
    })

    // subscribe to the streams, actions guarantee transaction semantics
    actions.forEach(
        ([stream, reducer]) => 
            stream.subscribe(action(event => {
                isRunningReducer = true
                state.set(reducer(state.get(), event))
                isRunningReducer = false
            }))
    )

    return () => state.get()
}
@luisherranz

This comment has been minimized.

Show comment
Hide comment
@luisherranz

luisherranz May 24, 2016

Member

I've been thinking about this a lot too. I really like the way code is written in Mobx but I prefer the Redux code organisation and pattern (Flux).

A while ago I had an idea to build a redux-like API on top of Mobx. I stopped because first I want to build a big app using redux/redux-saga and another one with mobx, which is what I am doing that right now with two of my projects.

Anyway, I'd love to share my thoughts with you in case they are of any interest. I've been thinking a lot about how the API would look like more than the implementation details, but I think it would be easily built with Mobx v2.

I don't want to hijack this issue so for anyone interested I've created a gist with my latest thoughts. Feel free to let me know what you think:
https://gist.github.com/luisherranz/afc77fe8e74e06dd0ed666a118d5b0ce

My plan is to keep improving and simplifying the API while I gain more and more experience with both Redux and Mobx, always looking for better easy-to-write, easy-to-reason, easy-to-test and no-boilerplate patterns.

Member

luisherranz commented May 24, 2016

I've been thinking about this a lot too. I really like the way code is written in Mobx but I prefer the Redux code organisation and pattern (Flux).

A while ago I had an idea to build a redux-like API on top of Mobx. I stopped because first I want to build a big app using redux/redux-saga and another one with mobx, which is what I am doing that right now with two of my projects.

Anyway, I'd love to share my thoughts with you in case they are of any interest. I've been thinking a lot about how the API would look like more than the implementation details, but I think it would be easily built with Mobx v2.

I don't want to hijack this issue so for anyone interested I've created a gist with my latest thoughts. Feel free to let me know what you think:
https://gist.github.com/luisherranz/afc77fe8e74e06dd0ed666a118d5b0ce

My plan is to keep improving and simplifying the API while I gain more and more experience with both Redux and Mobx, always looking for better easy-to-write, easy-to-reason, easy-to-test and no-boilerplate patterns.

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah May 24, 2016

@luisherranz

One problem with what you're presenting is that redux-saga only has so many advanced features because it's using generators. If you switch to async-await, you'll lose most of what the current saga implementation provides because async-await only deals with promises so it won't know what to do with .call() as just a single example as that doesn't create a promise.

AriaFallah commented May 24, 2016

@luisherranz

One problem with what you're presenting is that redux-saga only has so many advanced features because it's using generators. If you switch to async-await, you'll lose most of what the current saga implementation provides because async-await only deals with promises so it won't know what to do with .call() as just a single example as that doesn't create a promise.

@luisherranz

This comment has been minimized.

Show comment
Hide comment
@luisherranz

luisherranz May 24, 2016

Member

@AriaFallah thanks. That's not a problem as long as you are in the middle (saga.call can return a resolved promise if the function passed is synchronous) but maybe we should use the gist comments for that type of things and keep this issue only for when to use Mobx in contrast to Redux or possible implementations on top of Mobx and what improvements that may have.

Member

luisherranz commented May 24, 2016

@AriaFallah thanks. That's not a problem as long as you are in the middle (saga.call can return a resolved promise if the function passed is synchronous) but maybe we should use the gist comments for that type of things and keep this issue only for when to use Mobx in contrast to Redux or possible implementations on top of Mobx and what improvements that may have.

@xgrommx

This comment has been minimized.

Show comment
Hide comment
@xgrommx

xgrommx May 24, 2016

@mweststrate Your behavior and behavior in cfrp from @yelouafi pretty similar to join pattern in BaconJS
http://baconjs.github.io/api.html#join-patterns but with update method instead of just when Also you can take a look in yelouafi/cfrp#1

xgrommx commented May 24, 2016

@mweststrate Your behavior and behavior in cfrp from @yelouafi pretty similar to join pattern in BaconJS
http://baconjs.github.io/api.html#join-patterns but with update method instead of just when Also you can take a look in yelouafi/cfrp#1

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate May 24, 2016

Member

@xgrommx no join patterns are fundamentally different from transparently tracking deps. Because the first require you to declare, provide and combine the required dependencies, while the latter determine at runtime which observables are used and hence should be tracked.

It is like comparing JQuery with React. With both you can build a DOM, but with the first you specify which DOM nodes should be created, updated, replaced etc, with the latter you specify what the DOM should look like and it determines for you what DOM nodes should be created, updated or replaced to achieve that. Which significantly reduces boilerplate and cognitive load. See also: https://github.com/mobxjs/mobx/wiki/Mobx-vs-Reactive-Stream-Libraries-(RxJS,-Bacon,-etc)

Member

mweststrate commented May 24, 2016

@xgrommx no join patterns are fundamentally different from transparently tracking deps. Because the first require you to declare, provide and combine the required dependencies, while the latter determine at runtime which observables are used and hence should be tracked.

It is like comparing JQuery with React. With both you can build a DOM, but with the first you specify which DOM nodes should be created, updated, replaced etc, with the latter you specify what the DOM should look like and it determines for you what DOM nodes should be created, updated or replaced to achieve that. Which significantly reduces boilerplate and cognitive load. See also: https://github.com/mobxjs/mobx/wiki/Mobx-vs-Reactive-Stream-Libraries-(RxJS,-Bacon,-etc)

@yelouafi

This comment has been minimized.

Show comment
Hide comment
@yelouafi

yelouafi May 25, 2016

@mweststrate Thank, I'll give that a try.

One concern I have is with handling transaction from 'simultaneous events'. MobX is glitch free with regard to its internal dependency graph. But when connecting the behaviors to external event streams we may end up with situation when some root event (e.g. button click) cause 2 notifications (e.g. deselect a shape and select another shape). From the FRP POV those events are occurring simultaneously and need to be handled in the same transaction. However with the above example (If I m not mistaken) the 2 simultaneous events will trigger 2 consecutive transactions.

yelouafi commented May 25, 2016

@mweststrate Thank, I'll give that a try.

One concern I have is with handling transaction from 'simultaneous events'. MobX is glitch free with regard to its internal dependency graph. But when connecting the behaviors to external event streams we may end up with situation when some root event (e.g. button click) cause 2 notifications (e.g. deselect a shape and select another shape). From the FRP POV those events are occurring simultaneously and need to be handled in the same transaction. However with the above example (If I m not mistaken) the 2 simultaneous events will trigger 2 consecutive transactions.

@thomas-jeepe

This comment has been minimized.

Show comment
Hide comment
@thomas-jeepe

thomas-jeepe May 25, 2016

@yelouafi

If you just do

'''javascript

transaction(() => {
this.boxA.selected = false
this.boxB.selected = true
})

'''

Mobx will consider both changes "simultaneous" and call all dependcies (boxA's and boxB's) at the same time.

If it isn't in transaction, then boxA's dependencies will be called first then boxB's.

thomas-jeepe commented May 25, 2016

@yelouafi

If you just do

'''javascript

transaction(() => {
this.boxA.selected = false
this.boxB.selected = true
})

'''

Mobx will consider both changes "simultaneous" and call all dependcies (boxA's and boxB's) at the same time.

If it isn't in transaction, then boxA's dependencies will be called first then boxB's.

@monfera

This comment has been minimized.

Show comment
Hide comment
@monfera

monfera Jun 23, 2016

tl;dr it may not be just a style issue; FRP inspired libraries are a good match for interactive models, data visualization and games (ideally 60FPS applications) and I feel these are hard, convoluted or verbose to do efficiently with Redux; glad to be corrected.

@AriaFallah I've used various FRP inspired approaches and one of their benefits in general (incl. MobX, most.js, xstream, flyd, kefir, bacon, RxJS in particular) is that they allow you to construct a directed acyclic graph of data dependencies, where the result of each node is only dependent on the inputs it gets, with the possible fold of its own history (e.g. scan).

I usually work on analytics and/or data visualization, and these areas tend to have fairly deep data flow graphs. For a simple example, think of an event stream (e.g. stock prices via websockets) that gets aggregated into histogram bins and the histogram is plotted with SVG. Things like sample count will determine how many bins to even have; each incoming event falls into a bin; and for the plot, there's calculation of axis scale, axis tick resolution and possibly, outlier bounds, plotting standard deviations or confidence intervals, and plotting the bars and maybe individual points. Then the user can zoom or pan which impact the view, or the user can slide parameters such as day lags for a Moving Average Crossover financial indicator.

I know how to construct such deep dataflow graphs with FRP inspired tools, and one benefit is that you can smartly and relatively easily cache values, or apply on-line methods (e.g. updating variance incrementally). For example, you want immediately responding visuals as you tweak some model parameters and need bounds on recomputation time or want to retain resources e.g. DOM SVG elements, WebGL context, buffers, shaders etc.

But I don't know how I'd do this well with Redux. Any thought on how to handle a cascading chain of dependent actions with Redux that's efficient? As an example: sure it's possible to reduce the state from a series of various events, and I suppose memoization can be used to avoid unnecessary recalculations. Downstream, regenerating and/or caching a vDOM tree and DOM diffing it the React or snabbdom way can even preserve key resources (e.g. DOM elements). But in my experience, when you write highly interactive visualizations or games - as opposed to writing a TodoMVC app, a newsstream SPA or other CRUD - it's hard to justify going down the route of memoization and DOM diffing when the alternative, a DAG based dataflow, can be smart about what changed, and no work is needed if the changes don't justify it.

For example, a new data point necessitates that the axis scale be recomputed if it's outside the current bounds, and this in itself leads to downstream changes (tweening the axis, maybe switching to coarser ticks, recomputing certain statistics, perhaps reducing the level of detail on the plot). But if the new point is inside the bounds, none of this needs to happen. With Redux, I only know of memoization and the shouldComponentUpdate trick to minimize performance loss, but maybe I'm not considering a more idiomatic Redux way?

In summary, is it not the case that FRP inspired libraries such as MobX, most.js, flyd, xstream, kefir can serve a class of applications that would be hard to do with Redux, besides the less limiting differences you enlist, e.g. OO or not; verbosity; style and feel?

monfera commented Jun 23, 2016

tl;dr it may not be just a style issue; FRP inspired libraries are a good match for interactive models, data visualization and games (ideally 60FPS applications) and I feel these are hard, convoluted or verbose to do efficiently with Redux; glad to be corrected.

@AriaFallah I've used various FRP inspired approaches and one of their benefits in general (incl. MobX, most.js, xstream, flyd, kefir, bacon, RxJS in particular) is that they allow you to construct a directed acyclic graph of data dependencies, where the result of each node is only dependent on the inputs it gets, with the possible fold of its own history (e.g. scan).

I usually work on analytics and/or data visualization, and these areas tend to have fairly deep data flow graphs. For a simple example, think of an event stream (e.g. stock prices via websockets) that gets aggregated into histogram bins and the histogram is plotted with SVG. Things like sample count will determine how many bins to even have; each incoming event falls into a bin; and for the plot, there's calculation of axis scale, axis tick resolution and possibly, outlier bounds, plotting standard deviations or confidence intervals, and plotting the bars and maybe individual points. Then the user can zoom or pan which impact the view, or the user can slide parameters such as day lags for a Moving Average Crossover financial indicator.

I know how to construct such deep dataflow graphs with FRP inspired tools, and one benefit is that you can smartly and relatively easily cache values, or apply on-line methods (e.g. updating variance incrementally). For example, you want immediately responding visuals as you tweak some model parameters and need bounds on recomputation time or want to retain resources e.g. DOM SVG elements, WebGL context, buffers, shaders etc.

But I don't know how I'd do this well with Redux. Any thought on how to handle a cascading chain of dependent actions with Redux that's efficient? As an example: sure it's possible to reduce the state from a series of various events, and I suppose memoization can be used to avoid unnecessary recalculations. Downstream, regenerating and/or caching a vDOM tree and DOM diffing it the React or snabbdom way can even preserve key resources (e.g. DOM elements). But in my experience, when you write highly interactive visualizations or games - as opposed to writing a TodoMVC app, a newsstream SPA or other CRUD - it's hard to justify going down the route of memoization and DOM diffing when the alternative, a DAG based dataflow, can be smart about what changed, and no work is needed if the changes don't justify it.

For example, a new data point necessitates that the axis scale be recomputed if it's outside the current bounds, and this in itself leads to downstream changes (tweening the axis, maybe switching to coarser ticks, recomputing certain statistics, perhaps reducing the level of detail on the plot). But if the new point is inside the bounds, none of this needs to happen. With Redux, I only know of memoization and the shouldComponentUpdate trick to minimize performance loss, but maybe I'm not considering a more idiomatic Redux way?

In summary, is it not the case that FRP inspired libraries such as MobX, most.js, flyd, xstream, kefir can serve a class of applications that would be hard to do with Redux, besides the less limiting differences you enlist, e.g. OO or not; verbosity; style and feel?

@AriaFallah

This comment has been minimized.

Show comment
Hide comment
@AriaFallah

AriaFallah Jun 23, 2016

@monfera

Really well put, and a really good read. I found myself agreeing with everything you were saying as I read through.

AriaFallah commented Jun 23, 2016

@monfera

Really well put, and a really good read. I found myself agreeing with everything you were saying as I read through.

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate Aug 12, 2016

Member

Nice discussion and there are quite some external links to this. Doesn't need to remain open any longer I think. If somebody feels fur summarizing it in a blog post that would be 💯 (I would be too opinionated for that ;-))

Member

mweststrate commented Aug 12, 2016

Nice discussion and there are quite some external links to this. Doesn't need to remain open any longer I think. If somebody feels fur summarizing it in a blog post that would be 💯 (I would be too opinionated for that ;-))

@usergit

This comment has been minimized.

Show comment
Hide comment
@usergit

usergit Aug 18, 2016

@mweststrate It would be great to leave it open so others can chime in as the community grows

usergit commented Aug 18, 2016

@mweststrate It would be great to leave it open so others can chime in as the community grows

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate Aug 18, 2016

Member

Reopened :)

Member

mweststrate commented Aug 18, 2016

Reopened :)

@mweststrate mweststrate reopened this Aug 18, 2016

@Kamaraju333

This comment has been minimized.

Show comment
Hide comment
@Kamaraju333

Kamaraju333 Oct 18, 2016

@mweststrate can you explain how observable works internally and is state immutable in mobx?

Kamaraju333 commented Oct 18, 2016

@mweststrate can you explain how observable works internally and is state immutable in mobx?

@mattruby

This comment has been minimized.

Show comment
Hide comment
@mattruby

mattruby Oct 18, 2016

Member

Mobx state is not immutable. Here's a video that goes over how mobx works
: https://youtu.be/TfxfRkNCnmk there are many articles that go over how
mobx works.

On Oct 17, 2016 10:47 PM, "Kamaraju prathi" notifications@github.com
wrote:

@mweststrate https://github.com/mweststrate can you explain how
observable works internally and is state immutable in mobx?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#199 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIrctRRLdWyy37-04EHln2-tL1BJU3Qks5q1EFrgaJpZM4IKFcX
.

Member

mattruby commented Oct 18, 2016

Mobx state is not immutable. Here's a video that goes over how mobx works
: https://youtu.be/TfxfRkNCnmk there are many articles that go over how
mobx works.

On Oct 17, 2016 10:47 PM, "Kamaraju prathi" notifications@github.com
wrote:

@mweststrate https://github.com/mweststrate can you explain how
observable works internally and is state immutable in mobx?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#199 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIrctRRLdWyy37-04EHln2-tL1BJU3Qks5q1EFrgaJpZM4IKFcX
.

@Kamaraju333

This comment has been minimized.

Show comment
Hide comment
@Kamaraju333

Kamaraju333 commented Oct 18, 2016

@mattruby Thanks

@fourcolors

This comment has been minimized.

Show comment
Hide comment
@fourcolors

fourcolors Oct 31, 2016

@mattruby It would amazing if you could set some type of configuration to tell MobX to work as immutable. Except not immutableJS cause.. ain't nobody got time for that.

fourcolors commented Oct 31, 2016

@mattruby It would amazing if you could set some type of configuration to tell MobX to work as immutable. Except not immutableJS cause.. ain't nobody got time for that.

@mattruby

This comment has been minimized.

Show comment
Hide comment
@mattruby

mattruby Oct 31, 2016

Member

Check out mobx-state-tree.

On Oct 31, 2016 11:06 AM, "Sterling Cobb" notifications@github.com wrote:

@mattruby https://github.com/mattruby It would amazing if you could set
some type of configuration to tell MobX to work as immutable. Except not
immutableJS cause.. ain't nobody got time for that.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#199 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIrcrAw_crcmVkcKi18jyPl22OimU9pks5q5hH9gaJpZM4IKFcX
.

Member

mattruby commented Oct 31, 2016

Check out mobx-state-tree.

On Oct 31, 2016 11:06 AM, "Sterling Cobb" notifications@github.com wrote:

@mattruby https://github.com/mattruby It would amazing if you could set
some type of configuration to tell MobX to work as immutable. Except not
immutableJS cause.. ain't nobody got time for that.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#199 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIrcrAw_crcmVkcKi18jyPl22OimU9pks5q5hH9gaJpZM4IKFcX
.

@joelday

This comment has been minimized.

Show comment
Hide comment
@joelday

joelday Jan 14, 2017

EDIT: Will rearticulate this later once less drunk.

joelday commented Jan 14, 2017

EDIT: Will rearticulate this later once less drunk.

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate Jan 14, 2017

Member

@joelday regarding that last comment; MobX already has Knockout style computed observables? Or am I missing something here?

Member

mweststrate commented Jan 14, 2017

@joelday regarding that last comment; MobX already has Knockout style computed observables? Or am I missing something here?

@frankandrobot

This comment has been minimized.

Show comment
Hide comment
@frankandrobot

frankandrobot Jan 21, 2017

The momentum around mobx reminds me of when angular's two way data binding first came out. It really was a joy to use compared to the frameworks of the time. However the honeymoon eventually ended when large real world projects gave birth to the state soup problem. Devolper velocity further decreased whenever you had to open the angular internals, namely the compiler. I'm wondering out loud if developers are still in the honeymoon stage with mobx. Namely, what sorts of problems are encountered with mobx in large, complex apps? Seems too early to tell.

A second issue alluded to earlier is the state soup problem. One way data flow solves it. As far as I can tell, mobx brings it back. There's nothing to keep you from creating an app of interconnected objects with cyclic dependencies, right?

frankandrobot commented Jan 21, 2017

The momentum around mobx reminds me of when angular's two way data binding first came out. It really was a joy to use compared to the frameworks of the time. However the honeymoon eventually ended when large real world projects gave birth to the state soup problem. Devolper velocity further decreased whenever you had to open the angular internals, namely the compiler. I'm wondering out loud if developers are still in the honeymoon stage with mobx. Namely, what sorts of problems are encountered with mobx in large, complex apps? Seems too early to tell.

A second issue alluded to earlier is the state soup problem. One way data flow solves it. As far as I can tell, mobx brings it back. There's nothing to keep you from creating an app of interconnected objects with cyclic dependencies, right?

@hccampos

This comment has been minimized.

Show comment
Hide comment
@hccampos

hccampos Jan 21, 2017

@frankandrobot while you can do two-way data binding in MobX, you are not advised to do so. In fact, when using strict mode, all state mutations need to happen in the context of an action which means you can get a clean trace of everything that happened in the program, and why it happened, a bit like Redux. It is just that redux asks (but doesn't enforce) you to always return a new object while MobX lets you modify the state and keeps track of it.

That being said, it is left up to the developer to decide on the best place to put their actions. You can put them all in a single store/service, you can put them in different stores/services based on domain, or you can indeed sprinkle them everywhere and end up with a mess. Redux in this regard is more beginner-friendly (some would say noob-proof) in that it stipulates that all the state mutations always happen in the same place (reducer).

hccampos commented Jan 21, 2017

@frankandrobot while you can do two-way data binding in MobX, you are not advised to do so. In fact, when using strict mode, all state mutations need to happen in the context of an action which means you can get a clean trace of everything that happened in the program, and why it happened, a bit like Redux. It is just that redux asks (but doesn't enforce) you to always return a new object while MobX lets you modify the state and keeps track of it.

That being said, it is left up to the developer to decide on the best place to put their actions. You can put them all in a single store/service, you can put them in different stores/services based on domain, or you can indeed sprinkle them everywhere and end up with a mess. Redux in this regard is more beginner-friendly (some would say noob-proof) in that it stipulates that all the state mutations always happen in the same place (reducer).

@capaj

This comment has been minimized.

Show comment
Hide comment
@capaj

capaj Jan 30, 2017

Contributor

@frankandrobot

Seems too early to tell.

Have you tried writing a big SPA with MobX? I have and I certainly did not enter state soup problem. I keep my state minimal, derive anything that can be derived. Everyting is fast, smooth, readable and concise.

Contributor

capaj commented Jan 30, 2017

@frankandrobot

Seems too early to tell.

Have you tried writing a big SPA with MobX? I have and I certainly did not enter state soup problem. I keep my state minimal, derive anything that can be derived. Everyting is fast, smooth, readable and concise.

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Jan 31, 2017

@frankandrobot While mobx does not actively prevent the user from two way data-binding without useStrict(true) it is highly encouraged to use a one way data-flow like you suggested. Mobx doesn't change that. At work we're using mobx to build an SPA with complex, nested and dynamic forms and mobx has been a lifesaver so far. Even more so for team members who struggle a bit with functional coding style or redux.

marvinhagemeister commented Jan 31, 2017

@frankandrobot While mobx does not actively prevent the user from two way data-binding without useStrict(true) it is highly encouraged to use a one way data-flow like you suggested. Mobx doesn't change that. At work we're using mobx to build an SPA with complex, nested and dynamic forms and mobx has been a lifesaver so far. Even more so for team members who struggle a bit with functional coding style or redux.

@frankandrobot

This comment has been minimized.

Show comment
Hide comment
@frankandrobot

frankandrobot Jan 31, 2017

@capaj Any more details about your team? If you're telling me it's a one person team, of course, mobx works! Any framework works when it's only one person. Interested in seeing how the architecture holds up on a good size team (+4) on a large, complex app and over the long term. I've used redux before in this scenario and the architecture held up pretty well. @marvinhagemeister interested to know what your thoughts will be a year from now :-)

frankandrobot commented Jan 31, 2017

@capaj Any more details about your team? If you're telling me it's a one person team, of course, mobx works! Any framework works when it's only one person. Interested in seeing how the architecture holds up on a good size team (+4) on a large, complex app and over the long term. I've used redux before in this scenario and the architecture held up pretty well. @marvinhagemeister interested to know what your thoughts will be a year from now :-)

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate Jan 31, 2017

Member

We are working with ~18 people on a MobX based project for almost 1.5 year now at Mendix. Would not be surprised if Wix, Lyft or Microsoft teams are working with similar sized projects. Don't know much details about those however, as these are internal projects and I am not involved in them. If you search the reactjs reddit you'll find quite some testimonials from big development teams using MobX

Edit: forgot not in a critical place :)

Member

mweststrate commented Jan 31, 2017

We are working with ~18 people on a MobX based project for almost 1.5 year now at Mendix. Would not be surprised if Wix, Lyft or Microsoft teams are working with similar sized projects. Don't know much details about those however, as these are internal projects and I am not involved in them. If you search the reactjs reddit you'll find quite some testimonials from big development teams using MobX

Edit: forgot not in a critical place :)

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Jan 31, 2017

@frankandrobot Our current team for this particular project is around ~8-10 people (some part time freelancers). Perhaps the difference is that the redux community has settled on a few preferred ways of doing things (reselect, redux-saga,...) compared to MobX.

Nonetheless both redux and MobX are excellent for state-management. One can't go wrong with either.

I'm curious to what the future holds as well, especially when Proxy-Objects are supported everywhere (so close!).

marvinhagemeister commented Jan 31, 2017

@frankandrobot Our current team for this particular project is around ~8-10 people (some part time freelancers). Perhaps the difference is that the redux community has settled on a few preferred ways of doing things (reselect, redux-saga,...) compared to MobX.

Nonetheless both redux and MobX are excellent for state-management. One can't go wrong with either.

I'm curious to what the future holds as well, especially when Proxy-Objects are supported everywhere (so close!).

@luisherranz

This comment has been minimized.

Show comment
Hide comment
@luisherranz

luisherranz Jan 31, 2017

Member

Both Redux and Mobx are awesome state managers, but if I would have to bet for the future state manager that'd be mobx-state-tree. It has the best of both worlds (and even more).

Member

luisherranz commented Jan 31, 2017

Both Redux and Mobx are awesome state managers, but if I would have to bet for the future state manager that'd be mobx-state-tree. It has the best of both worlds (and even more).

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Jan 31, 2017

@luisherranz That looks very interesting. It's more like an "extension" for mobx though and not a replacement. In our case we've gone with a similar route. Our state has a tree shape and each model has a de-/serialize method which has been great when loading data from an api and constructing models. I'm really excited about the snapshot feature of mobx-state-tree though! That's the only thing I miss with mobx coming from redux.

marvinhagemeister commented Jan 31, 2017

@luisherranz That looks very interesting. It's more like an "extension" for mobx though and not a replacement. In our case we've gone with a similar route. Our state has a tree shape and each model has a de-/serialize method which has been great when loading data from an api and constructing models. I'm really excited about the snapshot feature of mobx-state-tree though! That's the only thing I miss with mobx coming from redux.

@hccampos

This comment has been minimized.

Show comment
Hide comment
@hccampos

hccampos Jan 31, 2017

hccampos commented Jan 31, 2017

@marek-sed

This comment has been minimized.

Show comment
Hide comment
@marek-sed

marek-sed Mar 9, 2017

Hi, we are building an app for displaying various data we getter from different IOT devices (sensors, cameras,.. ). Frontend has really minimal logic, basically it just displays data into various sections. And user can create some filters for data. The app is currently in redux but it seems like an overkill and there are some big issues with unecessary rerenders which would require lots of effort to fix. So we want to rewrite it and are deciding between, mobx or immutablejs + some cursor library.

The logic in app is really simple.
apply filter -> fetch data -> merge into state -> react render.

Is mobx a good match for such application, how does it perform when merging large jsons into state.

marek-sed commented Mar 9, 2017

Hi, we are building an app for displaying various data we getter from different IOT devices (sensors, cameras,.. ). Frontend has really minimal logic, basically it just displays data into various sections. And user can create some filters for data. The app is currently in redux but it seems like an overkill and there are some big issues with unecessary rerenders which would require lots of effort to fix. So we want to rewrite it and are deciding between, mobx or immutablejs + some cursor library.

The logic in app is really simple.
apply filter -> fetch data -> merge into state -> react render.

Is mobx a good match for such application, how does it perform when merging large jsons into state.

@mweststrate

This comment has been minimized.

Show comment
Hide comment
@mweststrate

mweststrate Mar 14, 2017

Member
Member

mweststrate commented Mar 14, 2017

@quangv

This comment has been minimized.

Show comment
Hide comment
@quangv

quangv Aug 1, 2017

You can use cycle.js in place of redux/mobx for a React app? I never thought to do this, is there anywhere I can read more about this?

quangv commented Aug 1, 2017

You can use cycle.js in place of redux/mobx for a React app? I never thought to do this, is there anywhere I can read more about this?

@janaagaard75

This comment has been minimized.

Show comment
Hide comment
@janaagaard75

janaagaard75 Jun 24, 2018

@GitCash send 0.02 BCH to @mweststrate

Really sorry for the hideous large banner below. This is first time I'm trying the new GitCash tipping bot, and I wanted to tip my favorite project, but obviously hadn't thought this through. Now this just looks like a bad ad for Bitcoin Cash. :-(

Update: The GitCash bot was banned from GitHub, so the message below has been removed. I also realized that everybody subscribing to this thread got the image in an email. Double bummer.

janaagaard75 commented Jun 24, 2018

@GitCash send 0.02 BCH to @mweststrate

Really sorry for the hideous large banner below. This is first time I'm trying the new GitCash tipping bot, and I wanted to tip my favorite project, but obviously hadn't thought this through. Now this just looks like a bad ad for Bitcoin Cash. :-(

Update: The GitCash bot was banned from GitHub, so the message below has been removed. I also realized that everybody subscribing to this thread got the image in an email. Double bummer.

@GitCash

This comment has been minimized.

Show comment
Hide comment
@GitCash

GitCash commented Jun 24, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment