Using Redux with Meteor?

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.

Using redux for data doesn’t make much sense to me as that overlaps too much with minimongo. However using it just for ephemeral state + action dispatch seems like a nice separation of concerns.

If any data is coming through a subscription then I wouldn’t touch that with redux.

1 Like

I wrote a blog post series here:

That goes over how we use redux at workpop inc.

8 Likes

I was just about to post a link to this - we’ve just been working through it, it’s an excellent set of posts, thank you for taking the time to write it.

@yinghao , that seems like a good structure to me. For the sake of discussion and learning I made this example that uses DDP directly instead of minimongo and tracker.

It’s really easy to update the store from the incoming DDP messages. I think this is still very ‘meteoric’, even though it may sidestep some of the meteor front-end features.

We had some discussion about it here :
https://forums.meteor.com/t/redux-ddp-no-minimongo/?source_topic_id=15649

1 Like

Great article, thanks for posting this. Is workpop fully transferred to React now? I saw the job posting for recat /full stack front end guy. You guys are doing awesome stuff there. I’m working on building my skillsets to working for a company like yours someday :wink:

It ends right when it gets to actions :frowning: - Is that where you draw the line? Make the mutations through actions otherwise the rootReducer will pick up the dispatch message? So for mutations you dispatch a function, and for UI state you dispatch a TYPE?

Do you guys use one file for all actions or actions per a module?

edit: nvm there’s more (blog post 3 doesn’t link to the next one fyi)

Whoa! What!?! WOW!

I really wanted to do that, and it was my original aim-- what about optimistic methods through Meteor.call()?

We’re headed in that direction!