ref: server.js, ParksContainer.jsx
The story begins at server.js. Here, we use express as web framework. When a request comes in, the server passes the request object to match, which is a function of react-router, to do the actual routing. The second argument of match is a callback function.
This callback function does the following:
- Respond to client immediately if there is any error or redirection.
- Access the wrapped react component (in this example app, this is a redux container) and assign it to comp.
- Access the predefined static function fetchData (this is where fetching actually happens) and assign it to func. If fetchData is not defined, then assign a resolved promise to func.
- Execute func and chain it with some custom functions and the responding action. By using renderToString, we can export a react component to html string.
// server.js
import express from 'express'
// ...
app.get('*', (req, res) => {
// ...
match({ routes: routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message)
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
const comp = renderProps.components[renderProps.components.length - 1].WrappedComponent
const func = comp.fetchData
? comp.fetchData({ store, limit, offset })
: Promise.resolve()
func.
then(() => store.dispatch(serverRender())).
then(() => {
res.send("<!DOCTYPE html>" +
ReactDOMServer.renderToString(
<MuiThemeProvider muiTheme={ muiTheme }>
<Provider store={ store }>
<RouterContext { ...renderProps } />
</Provider>
</MuiThemeProvider>
)
)
})
} else {
res.status(404).send('Not found')
}
})
})// ParksContainer.js
class ParksContainer extends Component {
static fetchData = ({ store, limit, offset }) =>
store.dispatch(getList({ limit, offset }))
// ...
}
p.s. I contributed to this project, which demonstrate how to build a server rendering react app.