I don’t know if I understood correctly your problem, but you shouldn’t manage the action in any component. In Flux you should manage it in a Store and in Redux, in a reducer (which is like a store, but with more restrictions).
So, in your case, when the user clicks on the PB2 play button, an action like this should be dispatched:
Dispatcher.dispatch({
actionType: 'PLAY_BUTTON_PUSHED',
id: '2'
});
In Meteor, the id should be ideally the _id of the Minimongo object representing that video.
Then, in a Store (which is not one of the components) you should change the state of your app:
// declare some state variables...
var isVideoPlaying = new ReactiveVar(false);
// create store with getter methods...
VideoStore = {
getIsVideoPlaying: function(){
return isVideoPlaying.get();
}
};
// register to change state when action is dispatched.
VideoStore.tokenId = Dispatcher.register( function(payload) {
switch(payload.actionType) {
case 'PLAY_BUTTON_PUSHED':
isVideoPlaying.set(payload.id);
break;
});
And then, in the component which is controlling the play and stop functions:
Template.VP.onCreated(function(){
var self = this;
self.c = Tracker.autorun(function(){
var isVideoPlaying = VideoStore.getIsVideoPlaying();
if (isVideoPlaying === self._id) {
self.playVideo();
} else {
self.stopVideo();
}
});
});
Template.VP.onDestroyed(function(){
var self = this;
self.c.stop();
});
It’s important to distinguish between Blaze and React when structuring your components and managing state changes.
React works in a way that it doesn’t matter if you re-render a lot of components. The virtual DOM takes care of it and makes the small amount of changes.
But in Blaze, if you invalidate a lot of reactive variables, a lot of components will be re-rendered, sometimes without reason.
So in React, it makes sense to have only a few components getting state. This works well with most React apps, where the data is got from a call to the server and has one entry point.
But again, in Blaze and Meteor, things work differently. Data is in our own client database it can be accessed in a more “horizontal” way. You should, actually, if you don’t want to cause a lot of unnecessary re-renders.
So, I don’t think structuring a Meteor/Blaze app with a React architecture is a good idea, unless you use Meter AND React.
If you are working with Blaze, you should get the “reactive state” in the nearest point possible (like my example above) to avoid re-renders. This has some drawbacks, because components becomes less reusable. But to make components reusable in Blaze, you can still use the controller pattern.
If you want to structure your components in a React way, there are two interesting options:
-
@timbrandin’s sideburns, which is Blaze like templates but compiled to React.
-
@arunoda solution: Blaze Plus which includes state/props in Blaze and minimises the renders automatically.
It looks like most of the people using Flux is going in that direction. I think it makes sense.
But no, Session was not designed for that purpose and it’s not a good fit. If you store complex objects in it, it will invalidate everything and cause a lot of re-renders.
I already have a very powerful API. You can set and get pretty much anything, and it is smart enough to mix everything together and invalidate only what has changed, even with objects, arrays, MiniMongo cursors, or functions returning objects, arrays and MiniMongo cursors.
You can do stuff like this:
// save plain state, which will become reactive automatically
AppState.set('isVideoPlaying', false);
// save some state from the database
AppState.set('videoList', function() {
return Videos.find({});
});
// mix new nested state with already defined states
AppState.set('videoList.isReady', false);
Meteor.subscribe('videos', function(err){
if (!err) {
AppState.set('videoList.isReady', true);
}
});
// save complex objects
AppState.set('videoAuthor', {
name: 'Peter'
image: {
url: 'http://www.yourserver.com/images/peter.jpg',
width: 300,
height: 200
}
});
// mix it with other objects
AppState.set('videoAuthor', {
publishedVideos: 12
}
// or
AppState.set('videoAuthor.publishedVideos', 12);
// and so on...
If the state is changed, only the minimum amount of invalidations will occur. For example:
AppState.set('videoAuthor', {
image: {
url: 'http://www.yourserver.com/images/peter2.jpg'
}
});
// or the equivalent...
AppState.set('videoAuthor.image.url', 'http://www.yourserver.com/images/peter2.jpg');
won’t invalidate things like videoAuthor.name
or videoAuthor.image.width
.
These are the getters:
AppState.get('videoList');
// => [{ id: 1, title: 'Video1' }, { id: 2, title: 'Video 2' }];
AppState.get('videoList.isReady'); // => true or false
AppState.get('videoAuthor'); // => { name: 'Peter', image: { url... }, publishedVideos: 12 }
AppState.get('videoAuthor.image.width') // => 300
Accessible as well automatically in Blaze:
<template name='VideoPlayerList'>
{{#if videoList.isReady}}
{{#each videoList}}
{{> VideoPlayer}}
{{/each}}
{{/if}}
</template>
<template name='VideoAuthor'>
Author name is {{videoAuthor.name}} and has published {{videoAuthor.publishedVideos}} videos.
</template>
<!-- or... -->
<template name='VideoAuthor'>
{{#with videoAuthor}}
Author name is {{name}} and has published {{publishedVideos}} videos.
{{/with}}
</template>
Something like this would give Meteor a simple way to have horizontal access to state, instead of the vertical approach of React.
I still want to build something with it before releasing anything. I am experimenting as well restricting the use of AppState.set only to callbacks of Flux actions, like Redux does with reducers.
Instead of exposing AppState.set
, restrict its use like this:
AppState.modify(function(action) {
switch(action.type) {
case 'PLAY_BUTTON_PUSHED':
this.set('isVideoPlaying', action.id);
break;
}
But the app I am building right now needs to be extendable via plugins, so I don’t know if too much restrictions would be possible.