Using Redux with Meteor?

This is what Redux has given my organization (Workpop):

A logical place to separate actions from views. What we leverage Redux for are ActionCreators.

Our Redux Store is super barebones. It contains UI states that are not complex. Examples being, “SHOW_ALL_JOBS”, “SHOW_PUBLISHED_JOBS” etc. UI States on top of things pulled down from a Meteor publication. We made a reactive store essentially a reactive-dict, to use this state in our components.

When it comes to data served by publications or something dumped into minimongo we interface with it business as usual. The Meteor Way.

Our UI components Dispatch action functions that call Meteor.methods (which we have restricted as the only way to interface with the server). If we want to keep some data in the redux store we do, but other than that everything that we’re used to.

Instead of tons of business logic in your UI code, you have a layer where actions are handled. In Meteor, its really all about actions.

If you REALLY want you could store the data you get from minimongo in your redux store to use the redux dev tools…but idk this is where things get really blurry. Its double the work to mgmt.

4 Likes

It’s unfortunate that React and Redux add a lot of new terms. The good news is that you don’t have to use them unless you want to. Blaze still works :smiley:

However, I would at least recommend learning about them to level yourself up as a developer. The actual tech (Redux/React) isn’t that important but learning the ‘philosophy’ of how they solve problems will help you be a better developer. It just gives you more tools in your toolbox.

The egghead video linked above is great! There’s only a few mins on hooking it up to React, it will show you what it is and how/why it works.

If you boil it down, these both heavily use a style called functional programming. Using this style allows you to break down problems into small chunks. If you’ve heard about code being ‘easier to reason about’, this is what they mean. It’s just easier to see what’s going on with a ‘thing’ at any given time. Usually this means very little magic as well.

@SkinnyGeek, I’ve picked out all the terms I do not understand or know from your last post:

I’ll try to break them down by referring to some great resources that are quick to consume. For a deep dive I recommend the ebook linked in a few posts above.

side effects
This is a term that means calling a function that does some other action outside of the function. For example calling jQuery to hide an element in a function. It can get really confusing if you don’t expect it, like this:

function addTwo(a, b) {
  $('some-el').hide();
  return a + b;
}

Here this function is very bug prone because you woudn’t expect addTwo to mutate the DOM, you just expect it to add two numbers. This also makes it hard to re-use or test. This brings us to the next one:

Pure functions
This is a function that doesn’t have side effects. The thing you want to strive for. You can’t always make them pure but if possible, all the things it needs are passed in as params and no side effects are made. If they’re pure it also means you can call them again and again and you will always get the same result. This is why Redux needs the ‘reducer’ function to be ‘pure’ for time travel… if it’s making db calls when re-winding in time it won’t work the same each time.

ajax calls
You def. want to learn about these in depth, so you understand what options you have. These most closely map to meteor methods without any built in authentication.

https://www.youtube.com/watch?v=fEYx8dQr_cQ

reducers
This is a function that redux uses to take the ‘old state’ and merge it a piece of the ‘new state’. The name comes from the reduce function (you’re not actually using Array.prototype.reduce as a reducer though). Here’s a great video on that (and other FP stuff) from a Spotify engineer whos great at teaching!

If you don’t read/watch anything else please watch this, it’s worth 9 mins of your time… I promise!

https://www.youtube.com/watch?v=Wl98eZpkp-c
https://www.youtube.com/watch?v=Wl98eZpkp-c


**action creator** and **action**

This is a silly term coined by Facebook. It’s just a regular function that creates an action which is another word for an object literal with a payload for flux to use.

In Redux an action is an object like this that tells that app something is happening like this:

{type: 'BUTTON_PRESSED', favColor: 'red', userId: 'u123'}

The type attr is what the ‘reducer’ watches for and the rest is just metadata that it can use (i’ll get to the reducer later but it just saves the payload data to memory).

To pass it to the reducer we just call dispatch and call it a day:

var user = Meteor.user();
dispatch({type: 'BUTTON_PRESSED', favColor: 'red', userId: user._id})

Now you could gather up all of that data in your UI, and if it was simple that wouldn’t be so bad. However,… if you can make your UI so ‘dumb’ that it just gathers a bit of data from the DOM and calls a function that would be great. Then you know what your UI is responsible for.

The easiest way to do that is to use a… you guessed it… function to process our data for us. We’ll use this one to create the same ‘action’ as above… notice it’s just returning that object at the end:

// returns {Object} to be passed to reducer
function setFavoriteColor(data) {
  var user = Meteor.user();
  return {
    type: 'BUTTON_PRESSED',
    userId: user._id,
    favColor: data.color,
  }
}

In real life it could parse a form and send all of the payload to the action creator to validate and save. Here we’re using a few fields so either way is ok. Now we just call this in the UI… easy and clean!

dispatch(setFavoriteColor({color: 'red'}))

store
The store is just a single place (in Redux) where all of the app state lives. You can put the app in any state you want by modifying this tree of data. The data tree is just a simple object literal with keys and values.

The most powerful concept is that when you want to change how your UI looks, you can modify the data tree and the UI will reactively update. At all times a ‘snapshot’ of how the app should look is right there in one place (as opposed to scattered about).

For example:

//  entire state aka store
{
  posts: [{desc: 'one'},{desc: 'two'}],
  viewer: {name: 'Adam B', userId: 'u123'}
  drawer: {
    isOpen: false,
    type: 'admin',
  }
}

If we want to open the drawer on a mobile app we think a bit differently… it’s not, use jQuery to add this class and hide that… it’s ok I need to dispatch an action to toggle the drawer.isOpen to true and I know it will slide open. So on the menu button you have an event handler that dispatches a dispatch(toggleDrawer) action, write a function to return an action:

function toggleDrawer() {
  return {
    type: 'TOGGLE_DRAWER',
  }
}

The reducer will watch for that type and flip the boolean:

// note a 'reducer' slices off part of the main state tree
// not shown here, it's optional but keeps these smaller we're
// only using this part of the entire state shown above:
//   {
//     isOpen: boolean,
//     type: 'admin' | 'user'
//   }

function drawerReducer(oldState, action) {
  switch (action.type) {
  case 'TOGGLE_DRAWER':
    // overwrite isOpen value
    var newState = _.merge(oldState, {isOpen: false});
    return newState;
  default:
    return oldState
  }
}

ok so the actions package up the data and send it to the reducer, which merges the old state with the new state. With Redux this is important because if it’s the same object the UI won’t update. If it’s a copy (aka new object) then it knows it changed and will trigger a UI (if connect is using the new keys).

once you wrap your head around it, IMHO it’s much more simple thatn Meteor because everything is just a function that returns something. The only parts not show are the ‘merge reducers’ function that tie the total store state together and the UI part with connect. To give you a taste of connect, here’s an example of a ‘main pane’ using the tree above to slide it open with CSS:

var MainView = React.createClass({
  render: function() {
    var clName = (props.isOpen) ? 'slide-open' : 'slide-close';
    return (
      <div className={clName}>
        <SomeView />
      </div>
    )
  }
});


// watch a few keys on the main tree and pass in as 'props' to comp.
function mapStateToProps(state) {
  return { isOpen: state.drawer.isOpen }
}

export default connect(mapStateToProps)(MainView)

So that seems like a really complex way to toggle a class but i’m trying to keep it simple for the example. Also you can render this UI from a bug report’s copy of the state tree, or render on the server and send the tree down on initial load, or even undo-redo… jQuery can’t do that.


> Tell me why we need yet another tech, Redux with React, inside of Meteor again?

At the end of the day certain tools make a job easier. I have a few apps with very complex UI and lots of ‘local’ state and with those two tools my apps are more bug free than ever! Like 10x + less buggy as well as easier to debug. It’s easier to debug because I know exactly how the data flows though (as a convention).

Blaze/Session/Template vars/ etc… get to a point of spaghetti where it feels like you’re playing whack a mole. You fix one thing and then 2 days later to realize it broke the app somewhere else unexpectedly. It’s very fast to create UI but you lose time as the app grows.

That being said it’s not a silver bullet. It just helps me a lot so i’m sticking with it, even for small apps (there are other shiny hipster toys like Om and Elm that are cooler and more fun but this combo gets work done).

:beers:

32 Likes

I wouldn’t even use classes when there’s no React State and you can just use a function (as you know). And for many things you don’t need or want to use setState anyway. Definitely with the Redux “pattern” there’s no local state with setState, so basically createClass or class X extends React.Component.

I think when showing someone React I only mention local state (setState) for super short term state changes like for dragging, everything else (and with Redux) I want in the store so I can save it.

1 Like

Yea I totally agree! I was hesitating on using the stateless components since it could make it look more ‘alien’ to those who haven’t seen it. Thanks for bringing this up! I like how it makes it more simple. Also having a pure function instead of a class feels more ‘react’ to me as well.

Here’s the UI bit using one of those. Note, this fixes a lot of the ‘designer’ problems as this is much easier for them to use too:

const MainView = (props) => {
  const clName = (props.isOpen) ? 'slide-open' : 'slide-close';
  return (
    <div className={clName}>
      <SomeView />
    </div>
  )
};

// or more terse with implicit return, note, no room for logic above return

const MainView = (props) => (
  <div className={props.foo}>
    <SomeView />
  </div>
);
4 Likes

I just found this thread searching on Redux / Meteor… and wow video you attached is great lol I just subscribed and plan on watching the rest of these JS videos. I didn’t know about array.reduce(); Also your redux meteor example is easy to follow and great, thanks for this.

2 Likes

Thanks @franky ! Reduce is one of those mysterious functions that seems to be useless until you wrap your head around it. Using map, reduce, and filter allows you to basically transform 90% of data without any other helper functions :smiley:

One you wrap your head around those the learning curve of Redux is fairly small (the thinking about how to solve a problem with functions is the hardest part).

3 Likes

map is mapping one input to one or more outputs, reduce is an aggregation or grouping, and filter is removing right? These terms refer to querying style functions.

1 Like

Yep that’s it! There’s also forEach/each but that screams “SIDE EFFECTS HERE”… but is usually ‘better’ than looping (less complex).

Map takes a list of A things and returns a B things (changes it)
before: ['a', 'b', 'c'] after: ['A', 'B', 'C']
Filter takes an array and makes a smaller array based on the predicate function (true or false to keep/remove)
Reduce … is complicated as it’s very flexible but typically merges data with each iteration…

You can pretty much replicate the entire underscore library with map and reduce only!

The most powerful thing about them is to take a function that was designed to work with one thing and transform a lot of things using them.

(trivial) Example:

// when reading this function you're just thinking about one item in isolation
function uppercase(item) {
  return item.toUpperCase();
}

names = ['adam', 'jane'];

uppercasedNames = names.map(uppercase);

The line of thinking is different that procedural but both accomplish the same thing:

// can only be used once in this context
var names = ['adam', 'jane'];
var uppercaseName = []

for (var i=0, n=names.length; i < n; i++) {
  var upName = names[i].toUpperCase();
  uppercaseName.push(upName)
}
3 Likes

Also here’s a slightly more complex example with something similar I just did… take data from the cordova contacts data structure and reformat it to work in my app. This means using different keys, filtering contacts without a phone number, and formatting the number so Twilio wouldn’t complain.

Here’s a similar example using the above methods. Note some use inline anonymous functions and others pass in a function from a utils library:

// we want this
[
  {firstName: "TIM", phone:"+1555222000"},
  ...
]

// from this data
someData = {
  foo: null,
  bar: null,
  contacts: [
    {name: "Tim", type: "user", number: "555 222 000"},
    {name: "Leah", type: "admin", number: "555 888 000"},
    {name: "James", type: "user", number: "555 111 000"},
    {name: "Mary", type: "admin", number: "555 333 000"},
  ]
}

admins = someData.contacts
  .filter(c => c.type == "admin")
  .map(cleanPhoneNumbers)
  .map(uppercaseName)
  .map(c => ({firstName: c.name, phone: c.number}))
3 Likes

Awesome example. Thank you. Here’s an article on this: http://elijahmanor.com/reducing-filter-and-map-down-to-reduce/

1 Like

One of the best threads I’ve read on this forum for a while! Thanks @SkinnyGeek1010 et al for the succinct explanation :smile:

1 Like

There’s a nice js functional library that might help some. The library is called Ramdajs. I find the functions to be a bit more powerful than underscore and lodash; more importantly, taking a functional approach has elevated my approach structuring problems.

http://ramdajs.com/0.19.0/index.html

3 Likes

Any recommendation for how to handle login state in redux, should we put it in store or handle that in the high level container? Thanks

1 Like

You can certainly handle it in a container with getMeteorData. The easiest way is to use the flux helpers package. You can then just call:

// watch the logged in user and trigger an action on change
trackViewer(CollectionActions.viewerChanged);

Any time the Meteor.user() data changes this will call any function passed in… ideally a Redux action.

If you need more fine grained control your could listen for a change and then call like this:

// in client code

Meteor.startup(function() {

      Tracker.autorun(function(computation) {
        var user = Meteor.users.find(Meteor.userId(), {fields: {"profile.foo": 1}})

        if (computation.firstRun) {
          return;  // ignore first empty run
        }
        // call your action creator here
        CollectionActions.viewerChanged(user);
      });

    });
}
2 Likes

I’ve just built a pretty large app with Redux, and thought others might want the whole time-travelling dev experience too: https://github.com/yinghaochan/redux-meteor

It’s a bit rough but I’d appreciate feedback!

  • Redux devtools / time travel
  • Hot module reload for reducers, so you can change client code anywhere without losing state
  • Server side rendering with store hydration
  • Dev stuff doesn’t get included in production builds
  • Everything React / Redux is in /client, so dev builds are almost instant
  • If you don’t end up using Redux, it’s easy to remove.
  • Webpack / browserify / react / react router from thereactivestack!
3 Likes

I’m also looking for the best way to integrate meteor and redux.
I created a repo demonstrating what I think is a good way:

  • just use reducers, not tracker
  • have one MeteorData component, where I fetch all the data that I need, then send it to the store.
  • components just access redux store, don’t need to know about meteor.

Hope it helps someone! It’s working well for me, but I would really love to hear any other opinion.

Cheers!

2 Likes

This is a pretty simple setup and you could wonder why you need Meteor at all, besides the easy setup of the backend and scaffolding of course that Meteor offers.

You might consider having @SkinnyGeek1010 flux-helpers on the side for auto updates of the store;

Have you looked at this? You could argue about having two stores on the client. But overall I have a better experience with it for example when updating the database not from the client (e.g. you want to mark a todo as checked for a general list not owned by 1 user).

I haven’t run into problems with it (that it’s out of sync), but I didn’t make a giant app or anything.

1 Like

@rolf That’s actually kind of what I was going for, making it easier to move to a different backend by centralizing all the logic / client-server interaction. I didn’t list that as a feature because I haven’t actually tried to do it, so I’m not sure if there are any huge edge cases.

// track the maxIndex and presenterIndex from the server
export function trackPresenter (id) {
  return Tracker.autorun(function (computation) {
    let show = Shows.findOne({_id: id})
    
      // update the store
      dispatch(setPresenter(show.presenterIndex))
      dispatch(setMax(show.maxIndex))
      
      if (show.ownerId === Meteor.userId()){
        // set the current slide to presenterIndex if current user is the presenter
        dispatch(setSlide(show.presenterIndex))
      }
    
  })
}

So theoretically we could just change trackPresenter() to use socket.io or something else without actually modifying the jsx. It’s also much more modular. And if tracker runs unnecessarily, we can do a deepEquals here and not actually dispatch any changes. Or we can dispatch conditionally with other checks like if(…=== Meteor.userId())

Also, Meteor-flux-helpers looks really cool. I’ll try it out! Sweet stuff, @SkinnyGeek1010

2 Likes
  1. Can you give a couple examples of how you use actions in your templates? Are you saying they essentially call ActionCreators in redux which then call out to your Meteor methods?
  2. What do you mean by this:

UI States on top of things pulled down from a Meteor publication. We made a reactive store essentially a reactive-dict, to use this state in our components.

You have a separate reactive store outside of Redux? Not following…

Do you actually use Redux lib or just emulate the pattern?


It seems that we could store a redux-like state on Session and it would persist through hot code reload and be similar. But maybe that’s crazy?

@rolf, thanks for your reply.
I´m looking for a simple solution, that puts Meteor as my backend, and react as the frontend.

That´s not only for the case I want to change the backend, I feel that the code became much more clear this way.

@dfischer, the idea is to use just Redux inside components. You get somehow the data you need from Meteor, and dispatch it to the store.
From your component, you just subscribe to this data, and use it like in a regular redux app.

I was thinking about a Tracker function, but the official packege is actual very simple, and I love official stuff…
That´s way I´m using an empty component, just to get meteor data and send to redux´s store.