isomorphic/universal react app for high performance mobile web application. support server side render spa support multi-page architecture without heavy react-router.
- react
- react-router (for spa route manage)
- redux
- immutableJS (optional)
- express
- ES2015
- webpack
- babel 6
- Service Worker cache static files
- Web Security support
- we recommend using ES6 module for tree shaking optimization.
- npm install react-ocean
- npm run build:dev // for development
- contains 'in-line-source-map' for debugging
- redux-logger
- redux-dev-tool(window.devToolsExtension)
- 'why-did-you-update' avoidable re-render checking
- react hot module replacing
- npm run build:prod // for production
- npm run build:lib // build libs file
- npm run build // both
- npm run start // start server
- register server route
router.get('/', getIndex);
- define appName and renderData for server render
module.exports = function (req, res, next) {
res.renderReactHTML({
component: <Page/>,
locals: {
appName: 'index',
title: 'index page'
},
data: fakeData,
rootReducer
});
};
- add a client page whose name is the same as appName
initializeRender({
rootReducer,
component: <Page/>
})
- todos
- async action
- chat room
<Route path="/" component={App} onChange={ onChange }>
<Route path="vote" getComponent={(nextState, cb) => {
require.ensure([], require => {
cb(null, require('./pages/App/Vote').default);
}, 'Vote');
}}/>
<Route path="about" component={About} />
</Route>
import connectDataFetchers from '../../utils/connectDataFetchers';
import * as ACTIONS from '../../actions/vote';
@connect(function mapStateToProps(state) {
return {
message: state.vote.message
};
})
@connectDataFetchers([ACTIONS.loadData])
class Vote extends Component {
static pageConfig = {
pageId: 'Vote'
};
render() {
return (
<div className="vote">
this is vote
<Link to="/about?debug=test">about</Link>
<Link to="/test">test</Link>
message: { this.props.message }
</div>
);
}
}
fetch data according to connectDataFetchers.
import fetchList from '../../../fetchList';
export const LOAD_VOTE_SUCCESS = 'LOAD_VOTE_SUCCESS';
export const LOAD_VOTE_FAILED = 'LOAD_VOTE_FAILED';
export function loadData(opts, req){
return (dispatch) => {
return fetchList.getVote(opts, req).then((resp) => {
dispatch({
type: LOAD_VOTE_SUCCESS,
payload: resp.data
});
}).catch(() => {
dispatch({
type: LOAD_VOTE_FAILED
});
});
};
}
- add docker support