A simple example for doing data fetching and rendering in a universal (aka isomorphic) app with React Router and Redux.
Clone the repo then:
npm install
npm start
The example uses the GitHub API for demonstrating how async Redux
actions can work together with React Router server side and client
rendering.
For the sake of simplicity it's also using
redux-actions and
redux-promise.
These are the most important aspects of this approach:
- universal: setting redux actions for data fetching as props on the routes, as seen here:
<Route path="/" component={App} action={actions.getContribs}>
<Route path="user/:name" component={User} action={actions.getUser} />
</Route>
- server: collecting all actions, dispatching them and waiting for all of them to settle before rendering the matched route server side, as seen here:
dispatchRouteActions: ({routes, params}, store) ->
actions = for route in routes when route.action?
store.dispatch route.action.call(this, params)
Promise.all(actions)
...
dispatchRouteActions(renderProps, store).then ->
props = assign {}, renderProps, {store}
element = React.createElement(RoutingContext, props)
html = renderToString(element)
app = appTemplate(html, store.getState())
res.send(app)
- client: dispatching a route's action when entering it by setting
the
onEnter
prop on every route that has anaction
prop, as seen here:
createRoutes: (routes, store, currentPathname) ->
dispatchAction = ({location: {pathname}, params}) ->
if pathname isnt currentPathname
store.dispatch @action.call(this, params)
currentPathname = pathname
createRoutes(routes).map set = (route) ->
{action, onEnter, childRoutes} = route
onEnter ?= dispatchAction if action?
childRoutes = childRoutes?.map(set)
assign {}, route, {onEnter, childRoutes}
...
routes = createRoutes(routes, store, document.location.pathname)
- if data is not loading anymore it's possible you exceeded GitHub's API rate limit of 60 calls/hour