Flux Example App with React & Meteor

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

@benjamin79

Hmmm that’s going to be a tough one. In Reaktor <Router> doesn’t pass it’s props to the children so I think you would need to do this in the mean time for it to work? Even then i’m not sure…

This is something that will be resolved eventually but I haven’t had the time to check it out yet. edit now that I think of it… once defaultLayout is implemented it should be able to resolve this since it would have to clone it’s props into the children.

  Reaktor.init(
    <Router>
     
      <Provider store={store}>
         {() => <Route path="/" layout={Layout} content={Home} />}
      </Provider>

      <Provider store={store}>
         {() => <Route path="/about" layout={Layout} content={About} />}
      </Provider>
     
    </Router>
  );

@benjamin79 I’ve used Redux with FlowRouter now and it’s easiest to use Redux with FlowRouter instead of Reaktor right now. You can pass store down as a prop to get it to work.

Perhaps I can add a PR later or @arunoda to allow the Reaktor root to pass store down to props.


FlowRouter.route('/about', {
  action: function(params, queryParams) {

    ReactLayout.render(MainLayout, {
      content: <About store={store} />
    })

  }
}); 
  

Then you can connect it up like:

About = React.createClass({
  render() {
    return (
      <div>
        <h2>Email from Redux: {this.props.email}</h2>
      </div>
    );
  }
});

// watch these keys for changes and add as props to About
function mapStateToProps(state) {
  return { email: state.test.email };
}

About = connect(mapStateToProps)(About);
1 Like

@SkinnyGeek1010

thank you, i will give it a try the next days.

It’s been a little while since this thread last left off. Do we now have a better understanding of the state of the art for integrating Flux and React into Meteor apps?

I’m starting a new project and it would be great to use some of this stuff! I just want to make sure I’m using the best tools for the job.

1 Like

@adrianmcli With meteor you can use flux to store local state (not in db). This helps keep structure and eliminates reactive-dicts and session.

You can also store minimongo docs in flux if that helps. You would watch the collection and fire changed events when the data changes.

At this point Redux is the king of flux libraries overall and also the most simple.

hey @SkinnyGeek1010, your redux example looks great. do you have any examples of how you’d use it while following the ‘package everything’ approach? I imagine you’d still want to keep each feature in its own package, and include any reducers + models for that feature inside the package. not sure if you’d want to keep actions for that feature inside the package as well, or keep them all in one file. as you said

This may seem like unneeded boilerplate but it’s really nice to have a file with all possible ways to mutate the state of the app.

@efrancis Thanks! I tend to split up reducers and actions by domain… so that could be a group of features. Kind of hard to know when/how to split them up though,

actions/
  app.js
  posts.js
  comments.js
reducers/
  app.js
  posts.js
  comments.js
store.js

// then require them
import {createPost} from 'actions/posts';

I haven’t tried the package everything approach. Personally I would leave Meteor before doing that… feels like trying to tap dance with galoshes :laughing: I’m now using Webpack to use ES6 modules but it’s kind of a hack to get it working (it basically creates a server/client bundle and drops them in the meteor folder, which meteor then uses). Still is nicer than globals and large app reloads are 1.5 seconds

You should be able to just export them though since they’re just functions.

My projects are structured exactly this way. Here are a couple of open source ones (still in early stages):

https://github.com/coniel/ibguides/tree/master/packages/app-forms/lib
I’m just getting started on that one (it’s a re-write of a PHP based site I run). At the moment the only package with actions and a reducer is ‘app-form’ (WIP), so have a look at that. I used the meteor dust boilerplate to get started (it’s designed as a starting point for large package only apps). One of the main ideas of meteor-dust is that there is a global App object, to which all packages have access. I created an attribute for actions (App.actions) and reducers (App.reducers), and then when creating the store, I simply get my reducers from App.reducers (see ‘app-core/lib/store.jsx’). Since app-core is basically where the app is built, you can be certain that all the reducers will be added to your store, as it’s the last package to be run. I also setup my layout with the in app-core. (note: I use a different createStore() for the server and client because I’m using SSR and I don’t want the server side render to have all the debugger stuff in it).

My other project is here https://github.com/coniel/coniel/tree/master/packages
It’s much larger and much messier (I’m planning to refactor it next week). There are a few packages still using Flux instead of Redux (I started out with Flux but then switched).

As a side note, I’m planning on releasing a few packages on atmosphere next week. I’ll probably start with my file-upload and forms packages. They are both designed for projects using React. They won’t rely on Redux or the meteor-dust way of doing things, but I’ll build add-on packages that will create and hook up Redux actions and reducers to them, so no extra work is needed for projects using Redux. (My goal is to turn all of my code into a library of fully featured yet easily extensible packages similar to the socialize packages.)

The file upload package basically provides an invisible <FileInput> component that will turn it’s parent into a file input that is hooked up to handle everything (the package also provides authorisation handling and an API for editing/removing files).

The forms package is a very stripped down version of aldeed:auto-form for React. It only handles form validation (it doesn’t build forms for you à la ‘{{> quickForm}}’ or do inserts/updates automatically). Basically you are provided with a <Form> component to which you provide an id, schema (aldeed:simple-schema) and onSubmit callback. The <Form> component will then attach the corresponding schema value (including label, placeholder, defaultValue…) to each child input based on it’s name. When a user attempts to submit the form, it first validates it and if there are no errors calls the onSubmit callback, otherwise it will add the error message as a prop to each invalid input. It won’t come with any UI elements for inputs, those will be in a separate package (I’m using Material UI so I’ll create a package for that). That way the main package stays UI agnostic and anyone can create an extension for it (similar to theme templates in autoform).

PS: @SkinnyGeek1010 what do you dislike about an all packages approach? I find it makes it super easy to test, maintain, share and re-use code compared to the regular Meteor way. Also having a package.js file is great to understand exactly what is part of a certain module and what it exports.

They’re very verbose to setup and still leave globals. I tried spiking a project using them but IMHO it’s not much better than using a file with a global object as a namespace and private variables. It’s also not exactly easy to share code without a way to pull in packages from Github or a private repo (meteorite was great for that). The one plus is that they control load order!

In general it seems like a hack to try and get real modules instead of using the JS ecosystem standard of modules.

The real issue is that MDG has been sticking their finger in their ears and saying ‘la la la la’ for years. I understand why Meteor didn’t ship with them as 2012 was uncertain for AMD/Browserify but Browserify has been stable for over a year now and started in 2012. Supposedly ES6 modules will hit core but I doubt they will come this year. Maybe 2016 :frowning:

2 Likes

What meteor package would you use instead of the npm ones you have on your meteor-flux-leaderboard repo?

Would kyutaekang:react-redux be sufficient?

1 Like

I would avoid doing so at all costs. Flux/Redux move quickly and they’re constantly out of date on Atmosphere. Most of them use meteorhacks:npm anyway so all you’re gaining is a slightly faster install at the cost of being locked in.

Just found it cleaner to use only Meteor package, but you score a point here.

1 Like

Yea i’ve found out the hard way. You save 2 mins now and lose 2 hours later :smile:

Also the React ecosystem lives on NPM so you’ll need to pull in other great packages like classnames and others.

Am still wondering about what’s the best way to propagate the event back to the view components. Let’s see in detail how the action on the “Add 5 points” button works in your very nice meteor-flux-leaderboard example.

  1. The event is bound on the button.inc here : https://github.com/AdamBrodzinski/meteor-flux-leaderboard/blob/redux/client/components/SelectPlayer.jsx#L16
  2. The interesting stuff starts where the onClick event is handled. We have ```store.dispatch(Actions.incrementScore(this.props.selectedId));
 3. The action incrementScore is triggered with the playerId passed in parameter. It updates the DB and return the action name 'INCREMENT_SCORE'.
 4. The action name returned is sent to the store (https://github.com/AdamBrodzinski/meteor-flux-leaderboard/blob/redux/client/store.jsx)
 5. The store use the reducers to process the action. https://github.com/AdamBrodzinski/meteor-flux-leaderboard/blob/redux/client/reducers.jsx#L44 The application state is in this case not changed.

Now it's a bit more tricky and would appreciate your help to explain this behavior, the way Flux and React are handled in Meteor.js. In this step, you mention in your comments : 

```normally in redux you would update and merge state here but since have minimongo to do that for us we'll just wait for the flux-helper to fire a COLLECTION_CHANGED dispatch after the increment update. Since we're doing that we'll just return the old state to prevent the UI from re-rendering twice.```

When is the COLLECTION_CHANGED event caught? Seems this is done via the ```this.sub``` in the AppContainer https://github.com/AdamBrodzinski/meteor-flux-leaderboard/blob/redux/client/components/AppContainer.jsx#L7 - right?

How would we handle the events if not using Meteor Collections?

As far as I understand, in your code, you're sending all the properties from top to bottom, through all React components. Correct? Isn't that a bad practice, making sending data more complex than it should? Even when regrouping each major component in a container?

Or would listening to changes directly in sub-component bring any problem on the long run and go against Redux, making harder to follow the flow of information? Maybe this is more of a personnal (or organisational) choice.

I think what is not so clear to me yet is the way we connect Flux and Reac.js. This seems to be done here in the AppContainer : https://github.com/AdamBrodzinski/meteor-flux-leaderboard/blob/redux/client/components/AppContainer.jsx#L20-L33. Have red the http://rackt.org/redux/docs/basics/UsageWithReact.html docs but any input will definitely help!

I'd be very happy to hear your thoughts and remarks about this!

I’ll get back with a much more though answer later but for now i wanted to mention that you can still use the Meteor mixin for all DB data and then use Redux for data that’s not persisted (local state that needs to be shared globally in a sane way).

The tradeoffs are that you’re more coupled to Meteor and the mixin this way. The flux helper will help you keep everything in once place so you can basically ignore minimongo. This is much slower to build and more complex but not much more than ajax reqs in a regular app. I think it’s also more performant due to batching.

i’ll try to get back to this tomorrow,
Cheers