This example repo shows how to serverside render React Single Page App with coren
$ git clone git@github.com:Canner/coren-example.git
$ yarn
test in development mode, with hot-reload
$ npm run webpack-server
$ node app.js
$ open http://localhost:9393
view source to see HTML
test in production mode, with real ssr result
$ npm run coren
$ npm run webpack-server
$ COREN_ENV=pre-production node app.js
view source to see HTML
- react-router v4
- redux
- redux-api-middleware
- use
RoutesCollector
to generate routes need to be rendered
// /src/Home
static defineRoutes({Url}) {
return new Url('/');
}
// /src/User
// server will execute db.users.find().execAsync(), get the data then generate multiple routes with path '/users/:id'
static defineRoutes({ParamUrl, db}) {
return new ParamUrl({
url: '/users/:id',
dataProvider: () => db.users.find().execAsync()
});
}
// /src/UserList
static defineRoutes({Url}) {
return new Url('/users');
}
- use
HeadCollector
to get title, description
// /src/User
static defineHead(props) {
const userId = props.match.params.id;
return {
title: `user ${userId}`,
description: `user ${userId}`
};
}
- customized
ImmutableReduxCollector
as a best practice in our team, we use immutable state in redux. so we modify some part of ReduxCollector
class ImmutableReduxCollector extends ReduxCollector {
appendToHead($head) {
$head.append(`<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>`);
// wrap state with Immutable, when rendered in HTML
$head.append(`<script>
window.__PRELOADED_STATE__ = Immutable.fromJS(${JSON.stringify(this.state ? this.state.toJS() : {})})
</script>`);
}
wrapApp(appElement) {
// if initialState collected from components is empty, we pass undefined to store , let store.getState() be the intial state in reducer
const store = createStore(this.reducers, isEmpty(this.initialState) ? undefined : immutable.fromJS(this.initialState));
const wrapedElements = react.createElement(Provider, {store}, appElement);
this.state = store.getState();
return wrapedElements;
}
}
in Component, we use definePreloadedState
return initialState this component need
// /src/UserList
// 透過 server 傳過來的 db 做出 query
static definePreloadedState({db}) {
return db.users.find().execAsync()
.then(list => ({
users: {
list,
fetched: true,
isFetching: false,
error: false
}
}));
}
node devServer.js