When should you use Session
and ReactiveVar
? I use Session
variable as a communication medium between components. Let's look at Stackoverflow for example.
I labeled three hypothetical components. Let's look at the filters
component. If you click Tags
then the Main
components will show its questions based on your favorite tags. To do this, I will set Session.set("Main/showTags", true)
when the Tags
button is clicked. In the Main component, I will have a helper function like below:
Template.Main.helpers({
posts: function() {
var isTags = Session.get("Main/showTags");
var isQuestions = Session.get("Main/showQuestions");
...
if (isTags) {
return Posts.find().sort({tags: 1}) // or something along the lines
} else if (isQuestions) ...
}
});
This has worked well in most cases but I've seen from a lot of places that I should refrain from using Session
and use ReactiveVar
. But If I use ReactiveVar
everywhere, then I would need the reference to all template instances. I expect it to work well between direct parent and children templates (ex. inside the main component, there could be VoteAnswerViewsButtonTemplate
) but how would you do it with ReactiveVar
when you want independent components to talk to each other?
Here's my final question. How can I appropriately use Session
and ReactiveVar
to keep the scope of components and make them communicate with each other as well? Also, if I use Session
the way I do now, am I polluting the global namespace unnecessarily?
Related Documents:
As far as I know, there is no built-in features related to Session
variables that differentiate them from a regular reactive dictionary global variable (like @Kyll stated) that you would declare, for instance, in your client.js
file. The only difference is the Session
"limitation" of being necessary accessible application wide.
I am very pleased to take advantage of this difference, when I use a reactive dictionary or reactive variables in a smaller scope. I consider I have three kind of scopes:
1 - Global scope. E.g. the current UI language, the UI skin. I use Session
for that. Simple and global data, not the kind that could be confused with anything else.
2 - A cluster of templates. Let's say, for example, that I create a page to generate and customize pdfs in my app. I will not reuse any of the components elsewhere. My cluster is a folder with three files, let's call them pdfgenerator.html
, pdfgenerator.js
and pdfgenerator_controller.js
.
I use pdfgenerator_controller.js
to extend the route with all the specifics.
In the pdfgenerator.js
file, I have several templates that I all use in the cluster. At the beginning of the file, I create a reactive dictionary pageSession
(similar to reactive variables) and I use it in all my cluster. It allows me to pass data among all my components.
3 - Local scope. Whether it is a single template or a reusable component, it is meant to work alone. I won't use Session vars for those either. I do not want to overcrowd the Session
name space. What I do is pass to my template every data I need to operate it during its instantiation.
It could be from Spacebars:
{{> mySingleTemplate data=myData}}
or using Javascript:
Blaze.renderWithData(Template.mySingleTemplate , myData, self.firstNode);
In the local scope case, I also use a reactive dictionary
or reactive vars
just to handle the reactivity happening within the single template. In this case, I try to avoid the situations where I need to reactively return data to the parent template. If I have to (i.e. I won't probably make a package out of it), I use a local minimongo collection declared as global in the scope of the parent template. This way, I can pass along informations from the reusable component to its parent.
Example: an upload template. I use minimongo to store the name, size, type, status and url of each uploaded file. The minimongo collection is shared between the parent form template and the child uploader template.
Bottom line: I only use Session
variables for basic and global information. If the data structure I need to share globally is too complex/large, I rely on a collection.
I am curious to know if I get it right, so this is as much an answer than a test to see if people agree with my way of doing things. All comments and advices are welcome.
Session vars get a bad rap. The truth is, they're just fine until your app gets big. You'll know when it's time to move away from Session vars when:
isColumnHidden
mean again?) Or, the session vars form natural clusters (3 session vars for tooltips, 5 for tags, etc.).So, how do you resolve each problem?
For the first example, create a package. For example, In one of my larger projects I create a rightSideMenu
and leftSideMenu
package. Each has their own ReactiveDict that I export to the global scope (eg rightMenu.RD.get('col1Hidden')
). This keeps the approach modular. I can completely rewrite my menu code, but as long as I still expose the ReactiveDict, the API stays the same. That said, I still use a Session var to show/hide the left & right menus. Why? because Session is the perfect use for menus. I don't want a hidden menu to persist through browser sessions (ever close something you didn't know how to reopen?), but I do want it to persist through routes. In other words, I want it to last for that browsing Session.
If you want the url to hold session info, you need to use params or query params. For example, if you've got a map with 100 markers & the user wants to send that page to his buddy with a specific marker selected, it makes sense that the url be something like url.com/marker20
. But why stop there? You could also include the lat & lng of the map center: url.com/marker/40.23432/122.2342
. Maybe that's too much, maybe not. You decide. In your example, this makes a LOT more sense than storing isTag
in a session var because it lets folks bookmark it, use the back button, share it, and change it without using their mouse (yes, your userbase is as lazy as I am).
An additional note on your current setup is that you're using flags for something that is mutually exclusive, leading to a conditional hell. If you've got 2 vars that can't both be true, stop & rethink the design. So, if I HAD to use session vars, I'd do something like:
currentTemplate = Session.get('filter')
{{Template.dynamic template=currentTemplate}}
In the end, you'll need to think about a lot more than just ReactiveVariables. What about dependencies? Not every module needs to access dependencies of every other module. Same goes for collections, methods, and even CSS. I say let it grow organically & when it hits you "hey, this is a component" then turn it into a package, export a variable, and keep it modular. Until that point, Session vars are fine.
Your issue is not polluting the global scope. Then why not make your own?
myComponentScope = {}; //Declare a global scope for this component
Then just place as many things as you want inside. For example,
myComponentScope.foo = new Reactive-Var('foo');
And if you want something that looks like Session
you can add Reactive-Dict:
meteor add reactive-dict
And use it this way:
myComponentScope = new Reactive-Dict(/* Optional name */);
The optional name makes the dictionary persistent across hot-code pushes.
The API for this dictionary is the same as Session
.
Thus, when you have something strictly local (one template), go for a Reactive-Var
.
When it's something multiple templates of the same kind share, go for your own scope.
If it's something so important all your application will need it, use Session
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With