Skip to content

A starter boilerplate for a universal webapp using express, react, redux, webpack, and react-hot-loader

License

Notifications You must be signed in to change notification settings

NogsMPLS/react-redux-universal-hot-example

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

React Redux Universal Hot Example


build status react-redux-example channel on slack Demo on Heroku

This is a starter boiler plate app I've put together using the following technologies:

I cobbled this together from a wide variety of similar "starter" repositories. As I post this in June 2015, all of these libraries are right at the bleeding edge of web development. They may fall out of fashion as quickly as they have come into it, but I personally believe that this stack is the future of web development and will survive for several years. I'm building my new projects like this, and I recommend that you do, too.

Installation

npm install

Running Dev Server

npm run dev

Building and Running Production Server

npm run build
npm run start

Demo

A demonstration of this app can be see running on heroku, which is a deployment of the heroku branch.

Explanation

What initally gets run is babel.server.js, which does little more than enable ES6 and ES7 awesomeness in the server-side node code. It then initiates server.js. In server.js we proxy any requests to /api/* to the API server, running at localhost:3030. All the data fetching calls from the client go to /api/*. Aside from serving the favicon and static content from /static, the only thing server.js does is initiate delegate rendering to react-router. At the bottom of server.js, we listen to port 3000 and initiate the API server.

Routing and HTML return

The primary section of server.js generates an HTML page with the contents returned by react-router. First we instantiate an ApiClient, a facade that both server and client code use to talk to the API server. On the server side, ApiClient is given the request object so that it can pass along the session cookie to the API server to maintain session state. We pass this API client facade to the redux middleware so that the action creators have access to it.

Then we perform server-side data fetching, wait for the data to be loaded, and render the page with the now-fully-loaded redux state.

The last interesting bit of the main routing section of server.js is that we swap in the hashed script and css from the webpack-stats.json that the Webpack Dev Server – or the Webpack build process on production – has spit out on its last run.

We also spit out the redux state into a global window.__data__ variable in the webpage to be loaded by the client-side redux code.

Server-side Data Fetching

We ask react-router for a list of all the routes that match the current request and we check to see if any of the matched routes has a static fetchData() function. If it does, we pass the redux dispatcher to it and collect the promises returned. Those promises will be resolved when each matching route has loaded its necessary data from the API server.

Client Side

The client side entry point is reasonably named client.js. All it does is load the routes, initiate react-router, rehydrate the redux state from the window.__data__ passed in from the server, and render the page over top of the server-rendered DOM. This makes React enable all its event listeners without having to re-render the DOM.

Redux Middleware

The middleware, clientMiddleware.js, serves two functions:

  1. To allow the action creators access to the client API facade. Remember this is the same on both the client and the server, and cannot simply be imported because it holds the cookie needed to maintain session on server-to-server requests.
  2. To allow some actions to pass a "promise generator", a function that takes the API client and returns a promise. Such actions require three action types, the REQUEST action that initiates the data loading, and a SUCCESS and FAILURE action that will be fired depending on the result of the promise. There are other ways to accomplish this, some discussed here, which you may prefer, but to the author of this example, the middleware way feels cleanest.

API Server

This is where the meat of your server-side application goes. It doesn't have to be implemented in Node or Express at all. This is where you connect to your database and provide authentication and session management. In this example, it's just spitting out some json with the current time stamp.

Getting data and actions into components

To understand how the data and action bindings get into the components – there's only one, InfoBar, in this example – I'm going to refer to you to the Redux library. The only innovation I've made is to package the component and its wrapper in the same js file. This is to encapsulate the fact that the component is bound to the redux actions and state. The component using InfoBar needn't know or care if InfoBar uses the redux data or not.

Images

Now it's possible to render the image both on client and server. Please refer to issue #39 for more detail discussion, the usage would be like below (super easy):

let logoImage = '';
if(__CLIENT__) {
  logoImage = require('./logo.png');
} else {
  logoImage = requireServerImage('./logo.png');
}

Styles

This project uses local styles using css-loader. The way it works is that you import your stylesheet at the top of the class with your React Component, and then you use the classnames returned from that import. Like so:

const styles = (function getStyle() {
  if (__CLIENT__) {
    return require('./App.scss');
  }
  const stats = require('../../webpack-stats.json');
  return stats.css.modules[path.join(__dirname, './App.scss')];
})();

That's a little ugly, I know, but what it allows is very powerful.

<div className={styles.mySection}> ... </div>

The Future

FAQ

Help! It doesn't work on Windows! What do I do?

Fear not. chtefi has figured out what needs to be changed to make it work on Windows 8.

How do I disable the dev tools?

They will only show in development, but if you want to disable them even there, set __DEVTOOLS__ to false in /webpack/dev.config.js.


Thanks for checking this out.

– Erik Rasmussen, @erikras

About

A starter boilerplate for a universal webapp using express, react, redux, webpack, and react-hot-loader

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 96.8%
  • CSS 3.2%