Where do I fetch initial data from server in a React Redux app?

Eugene picture Eugene · Apr 22, 2016 · Viewed 22k times · Source

I've started learning React / Redux and stumbled on something that is probably a very basic question. Below are snippets from my app with some code removed for simplicity sake.

My state is described by an array of sites which is empty by default. Later reducer will have LOAD_SITES action to load a different set of sites whenever user paginates to a different page but for now it's doing nothing. React starts by rendering PublishedSitesPage which then renders PublishedSitesBox which then loops over data and renders individual sites.

What I want to do is have it render everything with the default empty array and meanwhile initiate a "load sites from server" promise and once it resolves, dispatch LOAD_SITES action. What is the best way to make this call? I was thinking about either constructor of PublishedSitesBox or perhaps componentDidMount. But not sure if this would work - my concern is that I'll create an endless loop this way that will keep re-rendering. I guess I could prevent this endless loop in some way by having some other state param along the lines of "haveRequestedInitialData". Another idea I had is to simply make this promise right after doing ReactDOM.render(). What is the best and cleanest way to do this?

export default function sites(state = [], action) {
  switch (action.type) {
    default:
      return state;
  }
}
...

const publishedSitesPageReducer = combineReducers({
  sites
});

ReactDOM.render(
  <Provider store={createStore(publishedSitesPageReducer)}>
    <PublishedSitesPage />
  </Provider>,
  this.$view.find('.js-published-sites-result-target')[0]
);

...

export default function PublishedSitesPage() {
  return (
    <PublishedSitesBox/>
  );
}

...

function mapStateToProps(state) {
  return { sites: state.sites };
}

const PublishedSitesBox = connect(mapStateToProps)(({sites}) => {
  // render sites
});

Answer

S McCrohan picture S McCrohan · Apr 22, 2016

There's no reason for this data load logic to touch your React components at all. What you want here is for the return of the promise to dispatch an action to your reducers, which make the appropriate changes to the store, which then causes the React components to re-render as appropriate.

(It doesn't matter whether you kick off the async call before or after you call ReactDOM.render; the promise will work either way)

Something like this:

var store = createStore(publishedSitesPageReducer);

someAsyncCall().then(function(response) {
  store.dispatch(someActionCreator(response));
});

ReactDOM.render(
  <Provider store={store}>
    <PublishedSitesPage />
  </Provider>,
  this.$view.find('.js-published-sites-result-target')[0]
);

Your React components are consumers of your store, but there's no rule that they need to be the ONLY consumers of your store.