How to do subscription with React Router?


#1

I’m currently working on a stack using Meteor, react, react router, redux. I spent a lot of time on researching and practising how to do sub/pub on this stack. I found a way and it seemed easy to use, so I would like to share it here. There might be something can be improved. Welcome to share your thoughts. I appreciate all comments and suggestions.

Here’s the code. It simply subscribes posts when entering a route, and retrieves data when the subscription is ready. There will be loading text when data is not ready.

action.js:

export function fetchPosts(status, data) {
  return { type: 'FETCH_POST', status, data };
}

reducer.js:

export function posts(state = {
  status: 'pending',
  data: []
}, action) {
  switch (action.type) {
    case 'FETCH_POST':
      return Object.assign({}, { status: action.status, data: action.data });
    default:
      return state;
  }
}

Router:

//  ... ...
const subscribePosts = (nextState, replace, next) => {
  Tracker.autorun(() => {
    if (Meteor.subscribe('posts').ready()) {
      store.dispatch(fetchPosts('success', Posts.find().fetch()));
    }
  });
  // Call next() right after the autorun, so there
  // is no delay when entering the route
  next();
};

const routes = () => (
  <Provider store={store}>
    <Router history={history}>
      <Route component={Layout}>
        <Route path="/" component={Home} />
        <Route path="/postlist"
               component={PostList}
               onEnter={subscribePosts} />
      </Route>
    </Router>
  </Provider>
);

post_list.jsx

export default (props) => (
  <div>
    post list page
    {
      props.posts.status === 'success'
        ? props.posts.data.map((post, index) => {
            return (
              <div key={index}>{post.title}</div>
            );
          })
        : <div>loading...</div>
    }
  </div>
);

#2

I’m not sure if going back to handling subscriptions at the route level Iron Router-style is a good idea? These days most people use the container/component pattern (aka smart/dumb components).


#3

Agreed. You should definitely keep data loading out of your Router.


#4

@ryanswapp @sacha
Hmm, it looks like router level subscription. However, in React Router, we can do sub in any level of components because any levels of components can have onEnter events. For example in this case, if PostList has a child component, we can sub for either itself or its child component, or even both.

I don’t know if there’s any difference from subscribing in a container. What do you think?


#5

I’ve found that maintaining an app is much more pleasant if you keep data loading in container components. Routes should only do routing. If you keep the data loading to a single component you know exactly what data is being loaded for a particular component. When you spread data loading out into many places it becomes unmanageable in the long run.


#6

@ryanswapp Thanks. Do you know any other ways to use containers?

Since the createContainer method from Meteor guide is using Mixin, and React announced that Mixin is no longer recommended. https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html


#7

createContainer is perfectly ok. It’s only using the mixin under the hood. They will surely change the implementation if mixins aren’t supported anymore, but the api will stay the same.

react-komposer is also a valid option: https://github.com/kadirahq/react-komposer

it was the archetyp for createComposer and has a slightly more complicated api, but it is also more powerful. It provides a different set of komposer that enables to use other datasources as well - not only Tracker-based Meteor-Datasources. It can deal with redux, apollo (i think) and even with plain ajax-requests. It further contains helper functions like composeAll which enables you to chain different composers together and enables you to refactor and fine-tune your containers as you need.

In mantra, react-komposer is also used to define and inject dependencies, which is a powerful pattern as well.


#8

@macrozone Thanks. react-composer would be a great choice