Confused With Meteor's Best Practices: the Way They Are Now & the Way They Used to Be

Hey everyone.

This might get lengthy and wordy, so I apologize in advance. I feel like my thoughts are all over the place and I am having difficulty putting them into words. So I will do my best.

I’ve been doing a lot of reading on React JS lately because I figured I might as well get a head start on it now. My React readings stemmed off into other topics like the Flux architecture (Redux in particular) and other best practices like “smart” and “dumb” components and passing state from the top down. I was also particularly interested in the concept of a global store that would represent the entire state of the application.

I also finished reading the first draft(s) of the Meteor Guide, and it seems that it was influenced by many of the “React-like” design principals; in particular, how smart parent templates will retrieve data and pass it into dumb templates.

It’s all very interesting and all the concepts are very sound, but I couldn’t help but feel confused the more I read because I feel like we were already doing things like this in the early days of Meteor development before we decided this way was no good and best practices were changed… and now we’re kind of going back.

I am likely not being very clear right now, so I will break my thoughts/questions down into to two main topics:

Where to subscribe to data, and how to pass this data around

The Meteor guide mentions subscribing to data close to where you are going to use it; ie. subscribe to the data in the onCreated hook of your template, or with the React getMeteorData mixin. This makes sense to me; if you have a Follow button component/template, that button would be responsible for getting that data it needs to function properly. This component could then be put anywhere you need it.

But later on, the examples described the pattern of subscribing to all of the data you need on the root/parent smart component that would represent the “page”, and that component would pass down the data to all of the dumb child components. This seems to contradict the earlier point because you are no longer keeping the data subscription close to where it is being used. Imagine if that Follow button is 5 or 6 components deep. Furthermore, this seems very similar to the pattern of subscribing to data on the router like we were doing with Iron Router, before we all decided that was a bad idea. I fail to see the difference between subbing to everything on the router and providing a data context through that versus subscribing to that same data in the root page component/template. I mean, the page component is dependent on the route anyways and you are accomplishing the same thing. What am I missing here?

A Flux-like store to represent the state of the application

We used to use global Session variables to share state between templates, but we decided that was bad and hard to manage. We then came up with the pattern of putting reactive-vars or reactive-dicts inside components to represent these session/state values. This would keep things easier to manage and would not pollute the global scope.

Now, we have a lot of discussion about the Flux architecture, which has the concept of a global store that represents the state of the application. This state would be accessed at the root component, and then passed down to all the child components. The Meteor Guide has some allusions to this same concept in a few of the examples. This sounds like a very cool concept. Some of that time traveling stuff to help with debugging sounds incredible.

But I also feel like we were already doing something similar with Session, before we all decided it was bad idea and the best practice was putting those reactive values in the template. What is the different between Session and a Flux-like store? Could we not do the same with Session and put our “store” inside of that? We could even strictly access that Session inside the root/parent “page” component and pass the properties down to the child components. Why was having Session represent the state of the application considered bad before, but Flux is considered good now? Again, what am I missing here?

I am just really confused. As I am studying this stuff, it all makes sense and sounds amazing as I am reading it. But as I am working with these concepts, they seem to contradict each other and I am struggling with understanding what the “right” way to do things is. Both concepts sound really good! I hate that feeling during development where it feels like I am doing things the wrong way. I suppose it hasn’t clicked yet.

Any help would be appreciated. Thank you for your time.

3 Likes

I don´t tested my self but @luisherranz MeteorFlux looks good right know. Are you reread this one more time: A neat diagram comparing Meteor, Flux, and Relay ?

I´m in the process to convert my Blaze App with subscriptions and method calls for all over the place to a hybrid Blaze React MeteorFlux organization.

MeteorFlux was another one that I looked into. It looks really, really good. Documented nicely, too. I personally really like the Action Dispatcher and see that being useful whether you do Flux or not. I also looked in that ReactData mixin which sounds really awesome. (I apologize… I cannot remember the name of either author at the moment.)

But those contributed to my confusion somewhat, too. Nothing to do with the quality of the packages! Don’t get me wrong! Just the fact that these packages introduced me to the concept of sideways data loading and how Tracker’s approach to reactivity is much different than the functional Flux/React approach. Which lead me to discussions about how sideways loading is bad and the functional top down approach is better. But then there are other discussions about introducing sideways data loading to React and how it might be good after all. Everything just confuses me these days! haha.

hi, I get your feel because I have struggled for such things for some time.

unfortunately, it’s a contradiction with easy to use and easy to manage data. If we prefer to manage data easily, we need to collect them in centralized places and pass them down ( to reusable comp). If we prefer to use data easily, we fetch them where we need them (by smart comp).

Our front-end app is a tree ( forest, actually) of comps. In order to achieve max reusability, we hope to build as more as possible reusable comps, which care nothing about business logic. This way we will get a functional app ( without data and service plugged in). But, an running app must have business logic, so we have to input them in our functional app some where, but, keep this layer as thin as possible.

finally, I get my ‘practice’. my front-end app is split into 3 layers, pages, parts(sections), and comps.
roughly say, pages and parts are nodes near the very top of the app tree (in fact, page is the root). they are smart comps, care about data and business logic, and we need to keep them thin, often within 2 or 3 levels, which let us manage our data and logic in relatively centralized places. then, my comps are actually reusable comps (including pure comps), which care nothing about data, and only act as how their props and states are, and of course, they are reusable, testable, and can span a deep levels of the app tree.

I recently had to dive into 1-2 year old Meteor code last week and after working in Redux heavily I think I might have some insight. You’re right, the flux/redux store is just another global. The main difference is how the data is mutated and how you access it.

With Session you can just call set anywhere in the app, in the event handler, in the controller, etc… this makes it very hard to track were the changes are being made (no pun intended lol).

Also the app data is sprawled out with loose keys where as in Redux you have a single object tree of data. The entire state of your UI comes from the one tree of data (well it should anyway lol).

In Meteor, especially older Meteor that data could be coming from a lot of places, Session, Dicts, local collections, router contexts, etc… With redux it constrains you to only use that one tree for all your data (It’s a bit fuzzy for Meteor and DB data, you can store it here but also in minimongo).

If you really boil it down there’s a strict pattern to how data comes into your clientside app and how it flow through the system until it gets to the UI. Not so much with Meteor (though the guides are getting there).

Having template data helps, but then you run into the same problem react has… how can you send data from on child to another child… there’s no way to pass data. So you resort to a Session var to share it across templates. Then at best you’re creating your own way of sending data (which can be great but harder to onboard new people).

Could we not do the same with Session and put our “store” inside of that?

Yep! absolutely. I posted something like this while ago but to get the same benefits all you have to do is abstract your ‘data handling’ outside of your view, and make a facade so that you can only update/get data from the session in one spot. This will clean things up a lot without adding the complexity of another library.

2 Likes

I was hoping you’d reply, Adam! Thank you.

Yeah, thinking about it more, I suppose the main issue with Session was more so being able to set it everywhere, rather than the “sharing” of state per se.

I am also wondering how feasible it would be to implement an action dispatcher pattern around Session. Maybe extend Session so that it is able to have more flexibility with what it can store (I honestly have no idea if there are limitations or not) and only have your Session.set calls handled in one place. Then you have the flexibility of passing state down from parents (ala Flux) or accessing it wherever you’d like. Doing it this way you get the benefits of Tracker and Session for free, such as maintaining your state through code reloads, which might be really nice for debugging.

I mentioned this earlier, but I really like the action dispatcher pattern regardless of whether or not Flux is being used. It keeps the components thinner and keeps things DRYer.

I dunno… maybe I want to keep the writes to the state in one place but want the freedom Tracker provides and read from it wherever I please haha.

1 Like

Here’s an example of how you can wrap Session (or a dict) and use ‘action creators’ to dry up the view:

Redux for Blaze Example App

I think the main win is that when debugging you know to look in the action creator functions for mutating data and you get logging when in dev. With this example the action is calling set which is not much better than Session.set but larger apps could use a ‘reducer’ layer if that’s needed.

const _store = new ReactiveDict('store');

store = {
  get(key) {
     console.log('Store', key)
    _store.get('key');
  }
  set() {
  ...
  }
};


// on small apps dispatch could just log user event stream in debug
dispatch = function(...args) {
  if (__DEV__) {
    console.log(args);
  }
}


Actions = {};
// this example is just being saved to memory, not mongo (though you could here)
Actions.createPost = (post) => {
  const posts = state.get('postsData');
  posts.push(post);
  state.set('postsData', posts);
  analytics.track('add post');
  Mailer.sendNewPostEmail(post.name)
  return {type: 'CREATE_POST'}
};



// somewhere in view (destructure action module)
const {createPost} = Actions;

Template.PostsPage.events({
  'click .add-post': function() {
    createPost({name: 'test1', desc: 'foo bar'});
  },

  'click .add-post2': function() {
    // or auto log with 'dispatch'
    dispatch(createPost({name: 'test1', desc: 'foo bar'}));
  },
});

That use to be one of the great things about Meteor. There was only one way to do things for the most part. There were variations and best practices – but nothing close to this. This made the platform easily accessible to new-comers and the like. Everyone spoke the same technical language.

I worry even after we make the switch to React, things won’t be the same. The integration won’t be as tight as it was with Blaze. There will be a mismatch between the way React does things and the way Meteor does things – at least for a while. Where does Tracker fit in all this change, the heart and sole of Meteor? Is this going to get replace in favor of Flux to complement React? The future seems uncertain.

There is a conflict between functional programming style and the Meteor way. Sorry for the analogies, function programming is like a chain of events, where each link (function) in the chain acts upon the data. It flows in one direction like a water hose.

Yet, up until now, in Meteor you have the freedom to do anything you want with data – no rules. Which gives you flexibility and control, but also causes confusion to some if you don’t follow guidelines or structure.

The side-loading thing is where Meteor and Flux/React collide. In Meteor, since we have full control, we don’t have to go through the water pipe opening, we can just cut a hole in the pipe anywhere and add in the data if it’s easier for some reason.

But this breaks functional programming dogma and really undermines one of the loudest arguments in favor of functional programming – being able to back track your steps (traceability).

Some refer to this as a time machine, being able to retrace your steps, but it’s simply, using the analogy again, reversing flow back up the chain link, link by link (function by function), until you find the function that malformed the data.

Functional developers swear by this as it provides as sense of consistency, structure, predictability, and in theory saves time tracking down bugs. Yet here to functional programming, like most things, comes with tradeoffs. Functional programming is harder to learn for some. It requires you to think “off-nominal” from traditional development. Also, like in the case of React, a feature it can take more time/code to wire up than say in Blaze i’d say.

Using Sessions is not bad or hard to manage if you use name spacing and structure. You should be able to reason where the changes to a Session are coming from – you don’t need React or Functional Programming (FP) for that. Proponents of FP reason that figuring out where a mutable object changed is easier if you know the path it takes.

Also, you should only need to use a global when you’re passing data between two separate templates. Between a parent/child template, you have a couple of options.

Hope this helps a little.

3 Likes

Are you sure there’s not a way to pass data from one child to another child sharing a parent template?

Also, with react, you’d need a callback within a child component to call out to the parent, then direct the parent to call out to the sibling child component?

1 Like

But of course sessions don’t have to be sprawled out with loose keys – if we follow name spacing and give structure?

1 Like

Oh right, good point. Also sorry for the typo :stuck_out_tongue_winking_eye: If you push the state ‘up’ to the parent then you can pass it ‘across’ to another sibling. Using React Native i’ve found this hard to follow because the data is no longer in each component (another conn of Redux/flux).

I’ve also ran into issues when you can’t push data up any higher because of the router… when each route changes it wipe the React state away.

But of course sessions don’t have to be sprawled out with loose keys – if we follow name spacing and give structure?

You’re right. You don’t have to make a mess when using Blaze and Session but after working on my projects and other peoples apps i’ve found that it tends to decay. Just a quick one here and a quick hack there builds up. Especially with many team members working on the same codebase.

Also that’s another thing, with Session it’s hard to see what possible options are there until after it’s set. You could easily fix this by having ‘action’ functions that set session but i’ve often had to grep for Session.set just to try and figure out what could be set.

All of these could be solved by writing better code! I don’t blame Blaze or Session for some of my messy code, just myself :smiley:

How about using URL to store the state like @arunoda said in his previous article, what is you pro and con on that? Thanks

@SkinnyGeek1010 @aadams

how can you send data from on child to another child… there’s not way to pass data

Can either of you give a simple example of when you would want to pass some data from one child to another? (not just share data between the two children)

1 Like

That works to a point but you can’t share an array of posts in the url easily.

I suppose I was referring to sharing data and not passing a message directly to another component, I can’t think of a use case for that.

Typically I would dispatch an action (which is global) and then any other piece of the view could act on that data (perhaps a loading indicator knows it should pop open).

I asked because I couldn’t think of a case either.

btw, here’s how I solved the whole sharing data between children: Not propagating changes or synchronizing values but to actually share properties between children (or any component for that matter).

2 Likes

@manuel – I can’t. I just know it can be done. In all my Meteor experience, I’ve never once come across a situation in which I had to do this. IMO you just wouldn’t wire the application up like that.

@seanh I don’t think you would ever use a URL to “store” state, more like pass state from one template to another, a parameter of sorts.

Traditionally a URL is synonymous with a Template (or web page). Further, passing state from one page to another was often done via the URL. But with the advent of SPAs, the lines have blurred a little.

Anyhow semantics aside, with Iron Router you can pass state along from one Template to the next via the URL of course. Depending on the application, some prefer to do it this way (instead of a Session I suppose). If you set an application up in a way that allows you to, at any point in time, type a URL into the browser with a parameter (that means something) and render a Template that is functional (can perform operations that matter), then I guess doing that could bring value in some limited circumstances.

Yet sometimes the state of a Template can get so complex, for example depending on the state of many variables and situations or modes within a interconnected system, that simply passing a few arguments over a URL to a Template might not suffice. The objects that contain complex state are not be well suited to a simple string in a URL. In these situations you might need, maybe a URL parameter, and a few Session variables containing complex objects, etc., for the Template to make sense of its world when its called upon.

I stay away from passing anything over URL and simply use Session for Template to Template communication. Although, with server side routes in Iron Router passing parameters over the URL is a must of course.

I hope I didn’t jumble this all up for you.

Cheers

1 Like