Flux Example App with React & Meteor

Yeah the meteor community is great.

I’d be interested to see the minimal redux/meteor app code once you get it going!

2 Likes

That definitely is very very helpful. Thanks @SkinnyGeek1010 for that. BTW - most of the work buy you in github is what i am following as starting point, being a fan of flux architecture myself. :slight_smile: Thanks for this !!

~saurshaz

1 Like

How would you implement routing with Alt (or Flux in general)?

Well, I guess most route changes would be done though simple links, but what about redirects (e.g. redirect a user from ‘/login’ to ‘/’ after a successful login).

I’m assuming the redirect would be triggered by a “successful login” event dispatched from LoginActions. If so, where would you listen for this event? Would you create a RouterStore or something?

1 Like

This is a great question and I don’t really have a solid answer for you. I also couldn’t get an answer on SO or the React forums. However i’ve been using the NavigatorIOS router in React Native and a bit of FlowRouter so i’ve been tinkering with this for some weeks.

@arunoda was also looking into this area as well. I’m not sure if he came up with any additional information.

My general conclusion is that if you don’t care about abstracting the router out of your view and/or you don’t need to track the route history in your dispatcher, then I would just keep using it the normal way.

To elaborate on the second reason; you may want to call an action goToPage(...) so that it’s tracked in your dispatcher calls. With libraries like Alt you can serialize this history and send it back on an error (with TrackJS or similar). You can also use this dispatcher data for ‘time travel’ with Alt or similar flux libraries.

This brings up another issue. How can you trigger an action when a user clicks on a link? The easiest way to create a <Link page='home' />component for anchor elements and add a goToPage prop on a <Button /> element for buttons that go to a page. Both of these would just call the routing action on click.

Redirects can directly call AppActions.goToPage('home') instead.


[quote="coniel, post:15, topic:7416"] I'm assuming the redirect would be triggered by a "successful login" event dispatched from LoginActions. [/quote]

This sounds right. I think the general flow for an async login would go like this:

  • view calls AppActions.loginUser(data)
  • loginUser action calls meteor async login (doing in store is considered bad)
  • the login callback has two paths:
  • on error call AppActions.loginFailed which can handle the bad login,
  • on success calls AppActions.loginSuccessful

The view can listen for those events by registering with the AppActions store, or if that was getting too large an AuthAction/Store would not be unreasonable (and more fine grained). That way the login view can listen for an error and act upon it (as well as any other part of the app).

It depends. If you want to subscribe to the query params reactively I would create a RouterStore/Actions. You can call the FlowRouter API in the view of course but without the mixin it won’t auto update.

If so then either track the FlowRouter API and emit a changed action or in the callback (like a normal collection) or preferably in the action callback of the route just emit a RouterActions.changed event and pass in the data from FlowRouter. I’m looking into integrating an optional prop in Reaktor where you can pass in a changeAction={yourActionHere} and it would trigger any time the route data changes (actually just submitted an issue for that).

It’s also worth noting that in smaller apps just using regular links and doing all the login logic in the view(container) is just fine. Having the complexity only helps when you have lots of stuff going on in your app.

Does this answer your questions? I’m hoping to integrate Alt into React-ive Meteor this week sometime so that flux examples include routing and auth.

And the standard main question: hot code pushes
What you are using or going to use as persistent state container for stores ?
local storage, reactive dict, client side minimongo collections or there is some other way how to plug in our own surviving structure ?

1 Like

I haven’t got around to adding this yet but my plan was to do the following:

use the _onMigrate callback and take a snapshot of the app alt.takeSnapshot() to serialize all the stores into JSON and save them to a Session variable (or Reactive Dict if available).

  Meteor._reload.onMigrate("onMigrate", function() {
    var snap = alt.takeSnapshot();
    Session.set("app:currentSnapshot", snap);
    return [true]; // i think this is the syntax?
  });

and then in a client/_startup.js file bootstrap the app stores when starting up. This would emit change events for each store making the view re-fetch and re-render.

Meteor.startup(function(){
  // load data back into stores and fire 'change' event
  alt.bootstrap(Session.get("app:currentSnapshot"));
});

This allows you to revert to the same state once it re-loads. If you’re using any local state, then it’s going to be harder to save that. However you could do the same thing manually (prob. easier to keep it in a flux store).

If you’re using reflux you’re prob. out of luck, unless you write custom code to serialize data and re-hydrate on startup. There are also other flux implementations with timetravel/snapshots but Airbnb has been using Alt with good success so I have decided to stick with that.

Hi all!

Really love the idea of using Alt via Browserify to handle an app’s flux implementation.

I have been working on an all-packages app, and am wondering if anyone has implemented a solution that allows a single Alt instance to be used across multiple packages? My first attempts were unsuccesful, using a package like this:

Package.describe({
  name: 'app:alt',
  version: '0.0.1',
  summary: '',
  git: '',
  documentation: 'README.md'
});

// npm modules
Npm.depends({
  'externalify': '0.1.0',
  'alt': '0.17.1'
});

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.3');
  api.use(['cosmos:browserify@0.4.0'], 'client');

  api.add_files([
    'browserify/client.browserify.js',
    'browserify/client.browserify.options.json',
    ],
    'client'
  );

  api.export('alt', ['client']);
});

And a browserify file like this:

Alt = require("alt");
alt = new Alt();

The first package that required this package (with ‘alt’ globally exposed) worked fine, but the second package bugged out with the errror: ‘alt is not defined’.

My short term solution has been to implement Alt via Browserify on a per-package basis, but would love to know if anyone else has run into this as well!

1 Like

Hi Reyen, thanks for trying it out!

That’s a really interesting problem. Are you trying to package Alt so that it has a hard package dependency? Would it be an issue to just infer that Alt global is defined?

If it’s a new app and you’re after scope isolation, my first thought would be to use @jedwardsWebpack boilerplate to use ES6 modules or @trusktr 's module package (which may need tweaking over time).

Do you find that the all-packages way creates too much boilerplate? I tried this briefly but couldn’t get past it. Perhaps a generator would have helped.

I wish I could offer more, let me know if you find a solution! :beers:

If by second package you mean a package that required the package that requires alt (e.g: FooPackage requires BarPackage which requires the Alt package) then the solution is simple. You need to add api.imply(‘alt-package’) to the package to uses the Alt package. I’m actually doing the exact same thing as you (all-packages and Alt as a package). I have an app-lib package that requires stuff all my feature packages need (including Alt). This is basically what it looks like:

Package.onUse(function(api) {
    // Packages
    var packages = [
        // React
        'react@0.1.4',

        // Routing and layout
        'kadira:flow-router@2.0.1',
        'kadira:react-layout@1.2.0',

        ...

        // Utilities
        'reactive-var',
        'tracker',
        'coniel:meteor-alt'
    ];

    api.use(packages);
    api.imply(packages);
});

As I said, the important part is the last line: api.imply(packages); That makes globals exported by the packages you use in this package available to packages that use this package.

Another “pro tip” about using an all-packages structure (you might already be aware of it): when you add third party packages (using api.use(...)), always specify a version. Meteor has a weird way of selecting the package version if you don’t explicitly specify it, and will almost always include some really outdated version. I had some many headaches before I figured this out, and sometimes I forget to specify the version when I add a new package and then spend half an hour trying to figure out why it’ not working before I realise Meteor decided to include version 0.0.3 of a package that is already on version 1.2.5…

2 Likes

Hey @SkinnyGeek1010,

I am trying to package Alt so that it is available globally, and any package can create it’s own stores/actions relative to that global instance. In fact I want it to be a global, which will be useful for debugging etc.

The reason for all-packages is two-fold:

  1. Having ability to ‘whitelabel’ the app is a requirement of the build, so it can be re-compiled with or without certain features.
  2. Having both a web and mobile component, eg: only sending the mobile package to ‘web.cordova’

Yes there’s alot more setup, but I couldn’t see a better way to deal with the requirements.

Going to test a few options, I’ll report back if I can solve this one.

1 Like

Oh awesome, I have a similar structure actually, but I was including my Alt package in my app-core package (after api.use’ing app-lib). I’m going to try out your approach and see how that goes, should solve the problem by the looks of things!

And I wasn’t aware of that tip, thanks for that!

Also is there any chance your Alt package is available on Atmosphere? Would be neat to check it out

Thanks for the detailed reply!

I’m building a pretty large app with quite a complex route and UI structure (lots of nesting). Basically there are different main areas (dashboard, group, course), each main area has sub areas (group discussions, group calendar) and those in turn have their own sub areas (group calendar event). I have an AppLayout layout for the app, then each main area has it’s own layout (which is added to the app layout), and the sub areas add their own layout segments as well…

I wanted to build it in a way that makes it easy to build the layout incrementally based on the route, without having to write a lot of additional code to keep track of things. At first I thought of making a LayoutStore that would keep track of the current layout state and react to actions, but it ended up getting complex and messy pretty fast.

So then I thought I would just use react layout and build nested layout components (e.g. GroupCalendarLayout = <AppLayout><GroupLayout><CalendarLayout>{this.props.content()}</CalendarLayout></GroupLayout></AppLayout>) then call ReactLayout.render(GroupCalendarLayout ... <Event /> ...);. However, that caused all of the layout components to get re-rendered even if I switch from '/group/calendar/event/:id' to 'group/calendar/event-list' (rather than keeping the already rendered versions of <AppLayout> and <GroupLayout> and <CalendarLayout>). So that seems like a bad way to do it, as one of the main points of React is being efficient by re-rendering only what needs to be re-rendered.

Then I found a surprisingly simple solution that works nicely. I use Flow Router which has route groups, to which you can attach a trigger called when a route from that group is activated. So I basically have a chain of groups (I call them controllers): AppController, GroupController, GroupCalendarController. Each group (except the base AppController) adds a prefix to the route GroupController => ‘/group’, GroupCalendarController => ‘/calendar’. Then I use the entry trigger to render the layout/sub-layout for that group into the parent layout, with a check to see if the previous route was part of this group/sub-group (in which case I don’t render it)). It’s a bit hard to explain, so I’ll show you the code:

AppController = MainController.group({
    triggersEnter: [function(context, redirect) {
        if (typeof context.oldRoute === 'undefined') {
            React.render(<AppLayout />, document.body);
        }
    }]
});

GroupController = AppController.group({
    prefix: "/group",
    triggersEnter: [function(context, redirect) {
        if (typeof context.oldRoute === 'undefined' || (context.oldRoute.group && context.oldRoute.group.prefix.substring(1,8) !== 'group/')) {
            React.render(<GroupLayout />, document.getElementById('app-content'));
        }
    }]
});

GroupCalendarController = GroupController.group({
    prefix: "/calendar",
    triggersEnter: [function(context, redirect) {
        if (typeof context.oldRoute === 'undefined' || (context.oldRoute.group && context.oldRoute.group.prefix !== '/group/calendar')) {
            React.render(<GroupCalendarLayout />, document.getElementById('group-content'));
        }
    }]
});

(I still need to make a helper function to replace the long if statement)

It’s been working nicely so far, and I basically don’t have to write any extra code to keep track of things.

2 Likes

Combining Flux with Meteor sounds very interesting and I’m very eager to refactor my current (soon to be production) meteor-react application to incorporate this architecture.

There just seem to be a lot of different flux implementations, different way to hook into meteor (web pack, browserify, official react package etc…) and I’m worried that I may pick the wrong approach that will end up dying out.

What is your opinion on this?

1 Like

@ryenbeatty I just put it up: https://atmospherejs.com/conielo/meteor-alt-flux. Basically the only difference between our implementations is that I also publish alt to the server (as I’m planning on using SSR).

@SkinnyGeek1010 Doing an all package structure adds a little bit of boilerplate (mostly just having to define things in the package.js file), but the benefits are definitively worth it. You are in total control of load order, which is invaluable for large apps. Also, it’s nice to break down a large app into independent modules. The key is to make them truly independent, which can be a bit of a challenge. I’m trying to design my app in a way that would allow me to remove basically any feature module without affecting the rest of the app at all. I’m building a learning management system which has two main ‘feature types’, groups and courses. I have a module called ‘app-group’ which has the basic functionality of groups (main layout, group creation wizard…) but no actual features. I then create feature modules such as ‘app-discussions’ and ‘app-calendar’. Those modules have no connection to the ‘app-group’ module, even tough they will be features of it. The only connection ‘app-group’ has to those modules is an array of available feature modules so that it can create tabs for them in the layout. Now the benefit is I can use all of these feature modules inside of ‘app-course’ without touching them. I can create new feature modules (e.g. ‘app-feed’) which will automatically work in both groups and courses simply by adding them to the array of available feature modules.

On top of that, I have packages for things like file upload, authentication (login and registration), avatars (displaying and changing) that are all easily re-usable (they all use Alt so any package that uses them simply listens for the dispatches they sent out, e.g. the file upload package simply exports a component. When a file upload starts/completes/fails it dispatches an event to which the feature package responds accordingly).

I was actually thinking of removing the UI (which is based on my own app design) from these packages and publishing them on atmosphere. That way anyone building an Alt based flux app could add these common, ready made modules.

There is a great set of instructions on how to build package only apps created by Sacha Grey and others on hackpad: https://meteor.hackpad.com/Building-Large-Apps-Tips-d8PQ848nLyE. They actually turned it into a boilerplate to get your app started: https://github.com/coopermaruyama/meteor-dust.

@tarlen For flux I think Alt is the way to go. In the end all of these different flux implementations follow the same basic principles. Alt has the advantage of having very little boilerplate, and importantly for Meteor, the ability to take a snapshot of the current state (so that it can be restored on hot code pushes). As for including react, the official react (simply meteor add react) package is the safest way to go. The downside is that currently you can’t use any non Meteor specific react components as they mostly expect react to be required using browserify. However, the official react package is simply a collection of packages with stuff needed to use react. So you can always add react by yourself using browserify, and then include the rest of the packages as Meteor packages. Check out the documentation for Meteor + React: http://react-in-meteor.readthedocs.org/en/latest/

@coniel thanks for explaining the all-package approach, that makes sense!

I’ve had really good luck using any React component or regular NPM modules (like ClassNames) with the browserify package like in the guide. Or did you mean using the packages approach? I’m not sure how browserify would work in that context. Perhaps you could imply like with Alt?

Awesome! It’s really helped make things simple in my latest client app. It’s also being re-factored a bit at a time (using Alt). This is a great video explaining how to easily refactor a current app with React.

You’re not kidding. I spent several weekends reading and trying out todo apps :laughing:. IMHO the main contenders are Alt, Redux, Reflux, and Fluxible.

I’m on the fence with Reflux because it lacks a dispatcher and you can’t make an action creator if needed. Alt can do the same magic/terse action creation like Reflux but can additionally let you define an action creator function… handy for async. Alt has a lot of helpers to add as much ‘magic’ you want.

Alt

My favorite implementation. It seems like the perfect amount of boilerplate reduction without sacrificing the flux benefits. Constants are generated for you. Stores are easy to setup. Straight forward documentation and guides (actually it’s the best IMHO). Snapshots are easy to use for hot code reloads. An acquaintance uses this at Airbnb and it has been working out well for them.

Fluxible

A lot of thought put into serverside rendering. I haven’t used this much so I can’t comment too much on it. Seems to be pretty popular.

Redux

The new hotness. It’s API is almost ironed out with it’s approaching 1.0. Note, i’m not 100% familiar with this so I hope I don’t mis-speak. This technically isn’t flux but has all the same benefits and more (also the creators of flux like it).

It’s based around functional programming’s methodology to never mutate state. You’re always taking old state, copying it, changing the new copy and returning it. The entire app has one state object. This departs from having several ‘stores’ for each domain. Redux has 1 store. This can be hard to fit into 1 file in a large app so you can have several ‘reducers’ that can be used to split apart different areas of the app.

Redux has more boilerplate than Alt but constants are optional and there are helpers you can add if you prefer implicit magic over explicit.

It really has a nice set of developer tools but i’m not sure how well they work with Meteor, since it would need to be loaded via browserify instead of Webpack (you could use webpack outside of Meteor but that’s another project :laughing:)

If this seems interesting their 1.0 guide has a really nice intro that gradually gets more complex as it’s explained.

This Redux article is also really good

If I had to pick one right now for Meteor I would choose Alt. Redux is tempting but i’m not sure how stable it is and what kind of trade offs that it has in prod. I’ll be making a Redux meteor app soon to try it out though!

1 Like

I don’t understand what people think Flux has to do with routing in particular. Flux is just a pattern for the data flow cycle to and from stores, and as far as I understand you can route however you want on top of it, unless a Flux implementation somehow complicates your React element hierarchy.

In any case, I don’t think a flux pattern on top of Meteor is all that necessary, unless you have to connect to non-Meteor data sources as well. React devs are currently tossing around ideas to allow components to do “sideways data loading” from observables (rather than having a controller-view load data from flux stores and pass it down) which is basically the same pattern as loading data directly from Meteor collections via getMeteorData.

For instance, @SkinnyGeek1010, why write all that boilerplate to detect user changes when any component that needs to track the current user can mix in ReactMeteorData and return {user: Meteor.user()} in its getMeteorData()?

As far as routing with React the standard tool in the community is react-router, which is pretty good.

Personally I’ve always been skeptical of the necessity of flux boilerplate. I haven’t been on a project where it really seemed necessary to separate actions (let alone action creators) from store methods, preferring rather to just call methods on stores directly from components. In my own React-Meteor project I’m just doing sideways data loading from Meteor collections (though passing that data down from controller-views) and having user actions call Meteor methods. Maybe if I was on a large corporate project actions and dispatchers would actually make things easier, but I don’t know.

2 Likes

@coniel

The downside is that currently you can’t use any non Meteor specific react components as they mostly expect react to be required using browserify.

The lack of built-in support for require or ES6 import is holding the Meteor ecosystem back in exactly this way. Hopefully soon all Meteor-specific React components will switch over to using require or import directly.

2 Likes

I think the only advantage of doing this is for use cases using ‘time-travel’ to undo-redo. If a new page is considered different ‘state’ then it makes sense. Doing this also allows you to replay the user actions when debugging. At any rate I don’t think it’s a primary use case for flux.


[quote="jedwards, post:29, topic:7416"] In any case, I don't think a flux pattern on top of Meteor is all that necessary, unless you have to connect to non-Meteor data sources as well. React devs are currently tossing around ideas to allow components to do "sideways data loading" from observables (rather than having a controller-view load data from flux stores and pass it down) which is basically the same pattern as loading data directly from Meteor collections via getMeteorData. [/quote]

Side-loading with the Meteor mixin is easier. You can drop it in and code like you do in Meteor and things just work. The only con is that it doesn’t provide any rules/suggestions on how to structure changes in your app. It’s up to the dev(s) to make sure they’re not making a mess with how they change data/state.

Flux more about having a predictable control of data flow and re-renders. Rendering the data is only a part of flux. The meteor mixin only does re-rendering.

Most flux implementations (as far as I understand) will batch up updates before rendering to make it more efficient. The dispatcher also prevents cascading changes. The last time I used the Meteor mixin with subscriptions it would re-render more than it actually needed. One change in data caused 5 container re-renders and the 5 children had ~18 re-renders. This was just 5 list items (however manually using the data in a session var with the mixin only triggered 1 render).

I’ve also used side-loading in a React Native app and it’s not a panacea. It’s really nice at first but as the app grows it’s harder to reason about how/when data changed.

If i’m already using flux for other data, to me it’s mentally easier to keep track of the whole app state if everything is flowing through flux. Using both flux and the mixin would make things harder to maintain even though it’s slightly faster to implement. For me maintaining an app is the hard part, not creating the boilerplate/setup code.

Agreed. It’s very good. However i’m not sure how it will play out with Arunda’s SSR work. I also had troubles serverside rendering in Meteor.


[quote="jedwards, post:29, topic:7416"] Personally I've always been skeptical of the necessity of flux boilerplate. I haven't been on a project where it really seemed necessary to separate actions (let alone action creators) from store methods, preferring rather to just call methods on stores directly from components. In my own React-Meteor project I'm just doing sideways data loading from Meteor collections (though passing that data down from controller-views) and having user actions call Meteor methods. Maybe if I was on a large corporate project actions and dispatchers would actually make things easier, but I don't know. [/quote]

Creating the boilerplate really isn’t that big of a deal. Especially with libs like Alt where it handles the dispatcher and constants for you. In small apps using one AppStore and AppActions is prob. just fine.

Sideways loading works, there’s nothing wrong with it. However the lack of architecture is an issue in a larger app. The larger the code base and/or the more developers working on it, the larger the problem with not having an architecture for data flow. It’s not really apparent until it’s too late. Somewhere north of 40k-50k lines of code on in internal app and it started becoming a nightmare. Someone would change something and another part of the app would break (especially Session based state). There was also no way to predict if the user clicked this then X,Y,Z will happen. Debugging / changing the app felt like playing whack-a-mole.

That being said, side-loading with the mixin is easier. You can drop it in and code like you do in Meteor and things just work. It’s up to the dev(s) to not make a mess with how they change data/state.

Here’s a simple example of a db backed counter with Alt + Meteor. Way less than vanilla flux:

// watch a Meteor collection for changes
trackCollection(Counter, AppActions.counterChanged);

var AppActions = alt.generateActions(
  'counterChanged',
  'incrementCounter',
  'decrementCounter'
);


class AppStore {
  constructor() {
    this.bindActions(AppActions, FooActions);
    this.state = {
      counter: Counters.findOne()
    }
  }

  onCounterChanged() { // refresh store with minimongo data
    this.setState({counter: Counters.findOne() });
  }

  onIncrementCounter() {
    var newCount = this.state.counter.count + 1;
    var id = this.state.counter._id;
    Counters.update(id, {$inc: {count: newCount} });
  }
}


var CounterView = React.createClass({
  mixins: [ StoreStateMixin(AppStore) ],

  getStoreState() { // or you could manullay listen w/o mixin
    return {
       counter: AppStore.getState().counter
    }
  },

  render() {
    return (<div>Current Count: {this.state.counter.count}</div>);
  }
});

Yeah, using Session variables has always seemed like a huge risk.

As far as cascading changes, I haven’t had a use case where it seemed necessary to make one store react to another, but I would be cautious about doing that kind of thing in any application, preferring to explicitly state what updates for each action, unless that really seemed to require too much code duplication.

That’s interesting about batching changes, and I can see what you mean about being able to observe the data flow. It’s true the Meteor mixin might cause a lot of re-rendering, but one could just as easily create a mixin that essentially throttles rerenders.

Maybe flux implementations also auto-optimize the following case? In one app I had a map and list view showing hundreds of assets. At first whenever a single asset changed the getMeteorData was refetching all of those assets, passing them down, and re-rendering the entire view, which was really expensive. The solution I came up with was to use PureRenderMixin on all of the children and wrap each map marker/list item in a controller-view so that when its data changed only it would update. But if some flux implementations behave more or less like a dependency injector, they could probably handle this automatically without requiring a lot of developer implementation.

1 Like