Flux Example App with React & Meteor

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

So i’ve only had issues of this with Blaze… it might have been with Iron Router too. Not a good time :laughing: I agree it’s prob. a code smell if you need to prevent it.

This is interesting. Immutible.js is 1st class with Alt you should be able to use the PureRenderMixin for those nice === comparisons. I have yet to try this yet… seems like a hassle unless you need it (though not bad)

function TodoStore() {
  this.state = Immutable.Map({
    todos: Immutable.Map({})
  });
}

TodoStore.prototype.addTodo = function (todo) {
  var id = String(Math.random());
  this.setState(this.state.setIn(['todos', id], todo));
};

TodoStore.displayName = 'TodoStore';

var todoStore = alt.createStore(immutable(TodoStore));

One thing i’ve done in some cases is instead of having flux subscribe high in the hierarchy… scope it down and have several ‘container/controler’ components that subscribe to a subset… making it easier to control re-renders.

I’ve only been using flux in prod. for a little over a month or so but it’s already solving a lot of issues (it seems like the larger the app the more benefit).

Oh almost forgot, another cool thing with Redux and Alt is that they have dev tools to literally see the data flowing in a list. It’s almost like having a console.log for every side effect/ call that a click makes. I haven’t had a chance to use this in a large team but I imagine that would be a real time saver.

Yeah, even shouldComponentUpdate takes some amount of time so I’ve had to use the container/controllers when it isn’t sufficient (React docs call those “controller-views”).

Oooh, that does sound nice to have the built-in data flow logging. I guess I’ll be checking those out before long.

Yea that’s one of my favs :smiley: You could even send the entire serialized state as a payload after an error with something like TrackJS… then you can playback their actions up until the error to see what happened.

Here’s a queued up sample of Redux and it’s dev tools: Dan Abramov - Live React: Hot Reloading with Time Travel at react-europe 2015 - YouTube
and Alt’s chrome debugger (30sec video): Alt.js timetravel debugging - YouTube

Redux is one of the reasons using your Webpack solution would be worth it… hot reloading stores and components!

I have sent PR to SSR branch based on current master
Have fun :smiley:
Sad that fast-render seems to accept subscription only from that mixin call, or maybe there is some other easy way?
Mby @arunoda can help us there.

@shock yep just replied to it, thanks! I’m hoping if I move the subscription to the component it would pick it up. I talked with Arunoda earlier about flux integration and there’s supposed to be a way (at least eventually) to hook into it so we can use this: http://alt.js.org/docs/bootstrap to prime it on bootup.

Just released a Redux branch tonight…oh boy is it nice. I was really liking Alt but Redux is a whole new level :smile:


Here's the gist. Compared to flux it has a single store and is very functional. This ends up making it very easy to reason about!
import { createStore } from 'redux';

/**
 * This is a reducer, a pure function with (state, action) => state signature.
 * It describes how an action transforms the state into the next state.
 *
 * The shape of the state is up to you: it can be a primitive, an array, an object,
 * or even an Immutable.js data structure. The only important part is you should
 * return a new object if the state changes, instead of mutating the parameter.
 *
 * In this example, we use a `switch` statement and strings, but you can use a helper that
 * follows a different convention (such as function maps) that makes sense for your project.
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

// Create a Redux store that holds the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counter);

// You can subscribe to the updates manually, or use bindings to your view layer.
store.subscribe(() =>
  console.log(store.getState())
);

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
1 Like

Yes seems like we are close to SSR working there, mby if we pass serialized state in hidden input or so.
Or get some router API to make it easier.

Material-UI is looking promising in SSR too, with little warning cause browser css prefixes does not match server/client side. But other than that it renders well after tuning slightly package.js. I have not used flux there.

Thanks for your example.

Can you or @arunoda help me on where to put

<Provider store={store}>

when using Reaktor meteor-reaktor

From redux website:

React.render(
  // The child must be wrapped in a function
  // to work around an issue in React 0.13.
  <Provider store={store}>
    {() => <App />}
  </Provider>,
  rootElement
);
1 Like