This project utilize MERN (MongoDB, Express, React, Node) boilerplate from Hashnode
npm run start
- starts the development server with hot reloading enabled
npm run bs
- bundles the code and starts the production server
npm run test
- start the test runner
npm run watch:test
- start the test runner with watch mode
npm run cover
- generates test coverage report
npm run lint
- runs linter to check for lint errors
MERN uses Webpack for bundling modules. There are four types of Webpack configs provided webpack.config.dev.js
(for development), webpack.config.prod.js
(for production), webpack.config.server.js
(for bundling server in production) and webpack.config.babel.js
(for babel-plugin-webpack-loaders for server rendering of assets included through webpack).
The Webpack configuration is minimal and beginner-friendly.
MERN uses express web framework. Our app sits in server.js where we check for NODE_ENV.
If NODE_ENV is development, we apply Webpack middlewares for bundling and Hot Module Replacement.
We use React Router's match function for handling all page requests so that browser history works.
All the routes are defined in client/routes.js
. React Router renders components according to route requested.
// Server Side Rendering based on routes matched by React-router.
app.use((req, res) => {
match({
routes,
location: req.url
}, (err, redirectLocation, renderProps) => {
if (err) {
return res.status(500).end('Internal server error');
}
if (!renderProps) {
return res.status(404).end('Not found!');
}
const initialState = {
posts: [],
post: {}
};
const store = configureStore(initialState);
fetchComponentData(store.dispatch, renderProps.components, renderProps.params).then(() => {
const initialView = renderToString(
<Provider store = {store} >
<RouterContext {...renderProps}/>
</Provider>
);
const finalState = store.getState();
res.status(200).end(renderFullPage(initialView, finalState));
}).catch(() => {
res.end(renderFullPage('Error', {}));
});
});
});
match
takes two parameters, first is an object that contains routes, location and history and second is a callback function which is called when routes have been matched to a location.
If there's an error in matching we return 500 status code, if no matches are found we return 404 status code. If a match is found then, we need to create a new Redux Store instance.
Note: A new Redux Store has populated afresh on every request.
fetchComponentData
is the essential function. It takes three params: first is a dispatch function of Redux store, the second is an array of components that should be rendered in current route and third is the route params. fetchComponentData
collects all the needs (need is an array of actions that are required to be dispatched before rendering the component) of components in the current route. It returns a promise when all the required actions are dispatched. We render the page and send data to the client for client-side rendering in window.__INITIAL_STATE__
.
Client directory contains all the shared components, routes, modules.
This folder contains all the common components which are used throughout the project.
Index.js simply does client side rendering using the data provided from window.__INITIAL_STATE__
.
Modules are the way of organising different domain-specific modules in the project. A typical module contains the following
| - Post
| - __tests__ // all the tests for this module goes here
| - components // Sub components of this module
| - Post.spec.js
| - PostList.spec.js
| - PostItem.spec.js
| - PostImage.spec.js
| - pages
| - PostPage.spec.js
| - PostViewPage.spec.js
| - PostReducer.spec.js
| - PostActions.spec.js
| - components // Sub components of this module
| - Post.js
| - PostList.js
| - PostItem.js
| - PostImage.js
| - pages // React Router Pages from this module
| - PostPage
| - PostPage.js
| - PostPage.css
| - PostViewPage
| - PostViewPage.js
| - PostViewPage.css
| - PostReducer.js
| - PostActions.js
Assets can be kept where you want and can be imported into your js files or css files. Those fill be served by webpack in development mode and copied to the dist folder during production.
We use babel to transpile code in both server and client with stage-0
plugin. So, you can use both ES6 and experimental ES7 features.
MERN is released under the MIT License.