The purpose of this project is to use the latest web stack to create the most basic of idle games. If you don't know what an idle games or why do they matter read the following: http://www.ign.com/articles/2013/10/10/inside-cookie-clicker-and-the-idle-game-movement
Want to participate in the project ?? Feel free to grab an issue from the issue page and hack something out. Note we will not accept pulls that don't contain unit tests.
Based on react-redux-universal-hot-example
npm install
npm run dev
npm run build
npm run start
What initally gets run is bin/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.
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. You won't have to deal with webpack-stats.json
manually because webpack-isomorphic-tools take care of that.
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.
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.
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.
The middleware, clientMiddleware.js
, serves two functions:
- 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
import
ed because it holds the cookie needed to maintain session on server-to-server requests. - 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 aSUCCESS
andFAILURE
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.
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.
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.
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 = require('./logo.png');
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 = require('./App.scss');
Then you set the className
of your element to match one of the CSS classes in your SCSS file, and you're good to go!
<div className={styles.mySection}> ... </div>
The project uses Mocha to run your unit tests, it uses Karma as the test runner, it enables the feature that you are able to render your tests to the browser (e.g: Firefox, Chrome etc.), which means you are able to use the Test Utilities from Facebook api like renderIntoDocument()
.
To run the tests in the project, just simply run npm test
if you have Chrome
installed, it will be automatically launched as a test service for you.
To keep watching your test suites that you are working on, just set singleRun: false
in the karma.conf.js
file. Please be sure set it to true
if you are running npm test
on a continuous integration server (travis-ci, etc).
They will only show in development, but if you want to disable them even there, set __DEVTOOLS__
to false
in /webpack/dev.config.js
.