Using Redux with Meteor?

Whatever makes sense for your app. The easiest path is to only use it for local UI state.

However, if you’d like you can also listen for changes on the a collection and when they change, you can sync the current state of minimongo to a redux array. This means whenever data comes in from a publications your redux store is synced and react-redux can update the UI as needed. As the post above says, that repo will show you how to use the flux-helpers package to listen for changes and dispatch an action to sync.

The downside is that it’s more wiring, but you get more control, better performance, and de-coupling.

Yeah, I think Adam’s right in that Redux is primarily for UI state. A good example being a state like “MODAL_OPEN”. Meteor apps are very data driven, so you’ll end up seeing that your redux store is pretty barebones. Even so, I think it’s a great pattern due to the middle wares and tools it comes with. One thing to remember is Domain State, the data you store in mongo, is different than UI state…but you can interface with updating it through “redux-thunk”. Essentially the UI state store dispatches actions to change UI state and dispatches functions (side effects) to deal with domain state updates.

1 Like

Yeah since posting this I saw something @SkinnyGeek1010 wrote about relay being for domain state and redux for UI state. All the contradiction is confusing me now though :slight_smile:

2 Likes

Yeah since our relay is meteor live query and tracker, treat that the same way as react developers treat relay. The truth is, redux may be overkill to some people in Meteor. And there’s 1000000 opinions. If you like Dan abramov the creator, the concept of the project, then make it work in the meteor context. It just won’t be 100% as other redux users will be implementing it.

1 Like

Might help:

1 Like

I’m also really confused as to how Redux might work in a Meteor application.

For the sake of learning, here’s a branch I made from @SkinnyGeek1010 's Redux example. ( thanks skinnygeek). This branch uses only Meteor Methods and not pub/sub. One thing I noticed is you can have optimistic UI updates without Minimongo, example here. On an update event, let the reducer set the store as soon as an action happened, and also call the method. If the method returns an error the change will be reverted.

Is this approach manually doing some of the heavy lifting that Minimongo/Tracker provides out of the box?

One other aspect I’m confused about using Redux is the connect function provided by the optional add-on package called React-Redux. Checking out the source code it seems like the connect function’s main purpose is to diff a component’s current state with the new incoming state, and only render if necessary. This sounds similar to descriptions of Blaze functionality, is it ?
Without using the optional React-Redux library what happens? ( I wanted to try this as well in the example branch ).

Doesn’t your library violate the pure principle of redux? How are you going to time travel through states?

Yes, it does, as explained in the project’s readme file. Anyway, I like the ideia of having a single source of truth within a ReactiveDict and how it is mutated by actions/dispatching. Also, avoids Session abuse.

Yea even in the React community there is debate about where flux fits in. Eventually Relay may include transient state (local UI state), which would eliminate Redux.

However, even in my React Native app, Relay is too complex to bother. Using AJAX, a REST API, and Redux gets the job done.

For what it’s worth I delayed using Redux until I had too (by using container compoennts to fetch via AJAX and pass props down), but at some point it became really confusing as to where the ‘props’ were coming from and it was easy to break things if you moved a component in between the data and the component using the data.

In this case Redux greatly simplified the app. So in general if Redux make the app more complex, I would just roll without it. You can go pretty far with setState and passing that state object down to children.

Also worth noting if you want to use Redux 100% like ‘normal’ apps then you can use Meteor methods instead of AJAX to use the same pattern. This is also very useful for scaling issues or when you don’t need realtime data… remember just because you can subscribe to a realtime feed doesn’t mean you should. A lot of data can be just fetched one time. Redux make this super simple.

@SkinnyGeek1010 Where is the correct place to do method calls/ajax requests in redux?

basically anywhere except for reducers, as they have to be pure functions (ajax calls will produce side effects). However, the action creator function is a great place for side effects.

You could get more complicated and could use fancy middlewares to do these requests and reduce boilerplate but I like to keep it simple and readable.

Example:


export function updatePost(data) {
  // make side effects here and not in the view
  analytics.track('update post', data.id)

  // call API or call meteor method
  API.Posts.create(data, (err, res) => {
    dispatch({
      type: 'UPDATE_POST_LOADED',
      loading: false,
      payload: res.body
    })
  });

  return {
    type: 'UPDATE_POST_LOADING',
    loading: true
  };
}



// somewhere in the UI
dispatch(updatePost({id: 'u123', desc: 'Foo bar'}))

Note this is not doing optimistic loading but you can imagine how that might be done. It first returns an ‘action’ object to let the UI know it should start showing a loading indicator and the ajax call will eventually dispatch another ‘action’ object which the reducer picks up and inserts into the store.

Hope this helps!

4 Likes

@SkinnyGeek, I’ve picked out all the terms I do not understand or know from your last post: reducers, pure functions, ajax calls (kinda know about), side effects, action creator, fancy middlewares (kinda know about), optimistic loading, ‘action’ object, store.

Things use to be so simple with Meteor, I understood all the terminology and knew how to accomplish almost everything I needed, I could follow along with every thread, but now I feel like I’m back to square one. :frowning:

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

1 Like

@aadams We don’t need it. Redux is just a better way to organise your UI state/logic. It’s optional. Some of the terms relate to functional programming (pure functions, side effects), and some to Redux (reducers, action creators, middleware, stores, actions).

You can learn about Redux here: https://egghead.io/series/getting-started-with-redux
The basics of functional programming in javascript you can read here: https://drboolean.gitbooks.io/mostly-adequate-guide/content/

1 Like

Redux is better at organising than what exactly (inside Meteor)? Why is it better? Why do we need to change data flow techniques and tech because we’re switching out Blaze for React?

@aadams Redux isn’t specific to React, you can just as easily use it alongside Blaze if you wish.

2 Likes

I think SkinnyGeek1010 wrote in one of his posts the he thinks Redux is better for managing UI state.

How else can you manage UI state in Meteor apps:

  • Save values in Session or ReactiveDict
  • Save values in ReactiveVars that are owned by UI Components (Blaze template, React component etc.)
  • Save all the UI state to MongoDB documents

So in order to write a Meteor app you don’t need to know Redux, but learning and knowing about different ways of doing things and the pros and cons helps you to make better decisions for your app architecture if you are in a position where you can influence those decisions.

5 Likes

@sanjo, that sounds reasonable and logical. Yet, the tools Meteor has provided for managing state have worked great for me so far, how much “extra” do we actually get from using a Redux “store”?

Is it all about organization as some have implied? Do we only use Redux in place of Session and ReactiveDict? And I keep hearing about Relay, is that going to wipe out Redux or are they complementary?

These seem like good questions. If you have an insanely complex dashboard UI with lots of client-side only stated components with dependencies, maybe the overhead abstraction of Redux makes sense?
If you’re (a mostly single client) viewing/modifying data in a DB then perhaps it doesn’t add that much? I personally prefer the minimongo API rather than having to declare the various dispatch actions and binding to stores etc…

Every problem in computer science adds another layer of abstraction. Now you have two more problems :smile: or… more turtles below.

If it’s comparing to an AJAX way of doing things it’s a different discussion.

1 Like

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