A neat diagram comparing Meteor, Flux, and Relay

All app state must be stored in in MongoDB (Like the single store with Redux)
All state mutations are triggered through Meteor Method calls (Like actions in Flux / Redux)
Views get their data from Minimongo Collections (by subscribing to data and then using cursors) or the parent view.

Was actually implementing this for a project, but with redux as the client store. One big advantage of redux is the addons: thunk, devtools…

What about having the option of using redux instead of minimongo? each collection that you would store in minimongo would simply be in the store, and upon update (using dispatch), it would update the server as well.

Sanjo exactly, but I’ve taken this approach in Redux itself.

Domain State comes from Mongo via Subscriptions or Method Calls(dumping in local collections)

UI State comes from a Redux Store which is a Reactive Dict.

When domain state changes, The Store dispatches an async action which calls the Meteor method for the state change.

Then everything comes full circle.

I’ve been there. And that’s when I realised Flux/Redux doesn’t fit in Meteor as they are. They are defined for a React-only world without transparent reactive programming (Tracker).

I tried it. I tried to dispatch an action each time Meteor “did something magical” and make everything Action-driven like Flux/Redux. But Meteor is not Action-driver, it is Data-driven. So what happened is that I started adding a lot of boilerplate here and there. I had to manually keep the Redux-like store in sync with Minimongo or any other reactive source out there.

Finally, I decided to add reactivity back to my Redux-like store and all that pain and boilerplate disappeared. You just do:

State.modify('posts.items', (state = []) => {
  return Posts.find().fetch();
});

and reactivity keeps things simple again.

But still, you got flexibility to define it depending on stuff happening on your app:

State.modify('posts.items', (state = []) => {
  switch(Action.type()) {
    case 'INIT':
    case 'CATEGORY_UNSELECTED':
      return Posts.find().fetch();
    case 'CATEGORY_SELECTED':
      return Posts.find({ category: Action.category }).fetch();
    default:
      return state;
  }
});
1 Like

hey @luisherraz good to read you. What do you thinks of mobservable? … how fit it on Meteor? … is overlap with MeteorFlux?
I make a post about: Mobservable and Meteor 1.2.1 is possible?

Still trying to choose an archicture. At the end MeteorFlux is the only develop for Meteor.

Mobservable is a great package. It’s transparent reactive programming (yeah, Tracker) for the React world and it shows the advantages of TRP in terms of code which is simple to reason about and less prone to edge case bugs and even performance gains when used right (check this https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/ out).

The main differences I see between Mobservable and Tracker are two:

  • Mobservable has all state inside and Tracker has state outside.

  • Mobservable is sync and Tracker is async.

Both are related. Sync stuff is easier to debug than async stuff.

I tried to make my ReactiveState package sync because when you own all state, you can execute stuff synchronously:

State.modify('tasks.areEmpty', (state = true) => {
  if (State.get('tasks.items').length === 0)
    return true;
  else
    return false;
});

State.modify('tasks.items', (state = []) => {
  return Tasks.find().fetch();
});

When you are evaluating tasks.areEmpty you can stop at the State.get('tasks.items') and execute its modify first, make sure it is up-to-date and then continue with tasks.areEmpty. Functions are executed synchronously and only once. Perfect!

The problem with that is, once again: Meteor reactivity comes from many places, not only a single tree. For example, add another reactive source to the mix: Meteor.userId().

State.modify('user.hasPendingTasks', (state = false) => {
  if (Meteor.userId() && State.get('user.tasks').length === 0)
    return true;
  else
    return false;
});

State.modify('user.tasks', (state = []) => {
  let userId = Meteor.userId();
  return Tasks.find({ userId }).fetch();
});

If you want to make this work synchronously, you need to know if Meteor.userId() has already changed or is going to change before executing any of the State modifiers.

The sad truth is: you cannot know. Tracker reactivity has no priorities and is spread in many places along Meteor. So the solution is what Meteor people already did: make Tracker async and wait for every reactive source to invalidate its computations. Then start executing computations.

For that reason, I don’t think Mobservable is useful or has any advantage over Tracker in Meteor if you don’t want to throw a lot of stuff which is already working with Tracker.

For Tracker, this means sometimes functions are going to be executed twice or even more times until they reach an stable state. That’s the reason MeteorFlux actions have three phases:

  1. Register callbacks (sync non-reactive).
  2. State.modifies or Tracker.autoruns (async reactive, it keeps executing functions until everything is stable).
  3. AfterAction callbacks (sync after all reactivity has finished and is stable, non-reactive).

Explained like this, this looks like really complex stuff, but in the end coding with TRP is simpler and less prone to bugs. Most of the time you don’t have to know about how it works internally and that’s what makes Tracker and Meteor so simple to start with.

@luisherranz One think that I don´t undestand.
You calll MeteorFlux TRP but it follow the Flux pattern that imply imperative call actions froms component´s, say React. On the other side, with mobserver, you declare what ‘data’ is observable and what React componentes observe this data. To me this look ‘decoupled’/transparent reactive.

Sorry for the confusion. MeteorFlux has three phases, two of them sync and non-reactive (Flux actions) and another in the middle using async TRP (State modifiers, using Tracker).

By the way, I’ve just read this article from @mweststrate: https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.1m59bm20f

It’s implementation of chain execution is very interesting and similar to what I tried to achieve with ReactiveState. But to solve the unstable situations it introduces a concept called transactions which is very interesting and would solve the example I gave in my last post.

1 Like

That’s why im only using redux for UI state. I’m treating any domain state update as a “side effect”. With thunk you can dispatch functions to handle them, works pretty nicely.

1 Like

How do you manage when Minimongo gets updated automatically by Meteor due to changes in other client Minimongos?

All meteor related updates minimongo included are domain state.

If you’re writing methods with tons of side effects you’re def gonna see different reactions to changes.

UI state is like visibility filters, active states, random stuff you’re already doing with reactive vars.

Is that what you were asking?

1 Like