React server side rendering seeder using ReactDOM/server and Express, state management using React Redux and Redux Thunk. Support initial async process or fetching, unit test using Enzyme and Jest, style pre-processor using SASS / Stylus supported.
git clone https://github.com/triadiprabowo/react-ssr-seeder.git && cd react-ssr-seeder
npm install
npm start
- Start development server (SSR-Enabled) on localhost port 3000 (default)npm test
- Start unit test with jest
npm run build:prod
- Build dependencies with production environmentnpm run start:prod
- Start production server using PM2npm run restart:prod
- Restart production servernpm run stop:prod
- Stop production server
Located in package.json, you may edit to accepted environment used in this project.
Environment Name | Default Value |
---|---|
NODE_ENV | development |
SERVER_PORT | 3000 |
Name | Object | Value |
---|---|---|
_PRELOADED_STATE_ | window |
Initial State from onInit method called in router |
staticContext | this.props |
Initial state from onInit method called in router (accessible from server side only) |
_isBrowser_ | none | true or false (get client platform) |
This project used style guidelines or project structure similar like Angular project, you may check Angular.io or Angular Style Guideline, main differences are we are using *.action.js
*.reducer.js
as our service and model.
src = root folder
|____browser = browser (client-side) react initialization including redux store for client-side
|____server = server (server-side) rendering and react initialization
|____app = application folder
|_____pages = pages folder (whole page component)
|_____shared = shared folder contains reducer, components (containers), actions, etc.
|_____reducers.js = reducers from shared and pages folder, include here for new reducer
|_____routes.js = router config
|_____server.store.js = Store will be used in server-side render (../server/index.js)
You must include onInit
in src/app/routes.js to run initial async operations.
import ImportedComponent from '...';
import exampleAction from '...';
// react-router options
{
path: '/',
exact: true|false,
component: ImportedComponent,
title: 'Page title',
description: "Meta Description",
onInit: (dispatch) => exampleAction(dispatch)
}
Create action within pages folder example index.action.js
, you may look example at src/app/pages/index/index.action.js
.
import Q from 'q';
export function getData() {
return (dispatch) => {
// ... action here
}
}
export function getData2() {
return (dispatch) => {
// ... action here
}
}
export default function OnInit(dispatch) {
// ...
/* for multiple request or async process use Q
** example:
*/
Q.all([getData()(dispatch), getData2()(dispatch)].then((data) => dispatch({ ... }));
// because we use redux-thunk so initial call procedure is like this-> actionName()(dispatch) to make sure initial state set properly on client-side
}
class ExampleComponent extends React.Component {
constructor(props) {
super(props)
// initial state
let initialState;
// check client platform if it is browser get from window object
if(__isBrowser__) {
initialState = window.__PRELOADED_STATE__['ExampleReducer'];
// dont forget to delete preloaded state after initiated
delete window.__PRELOADED_STATE___;
} else {
initialState = this.props.staticContext['ExampleReducer'];
}
// set to react state or any other methods
this.state = { ...initialState };
}
}