npm.io
5.8.8-SNAPSHOT • Published 7 years ago

interaction-player

Licence
private
Version
5.8.8-SNAPSHOT
Deps
29
Size
793 kB
Vulns
0
Weekly
0

Redux-side-effects driven react boilerplate

This is a very simple example to illustrate the usage of redux-side-effects with react. Together with the pre-configured development tools like eslint, webpack, and testing frameworks like karma, mocha, and various other packages, it can be used as a basis for coding new React/Redux projects.

  1. Usage
  2. Development
  3. Using immutable for redux store state
  4. Passing environment variables to client
  5. Server source code
  6. Client source code
  7. Application URL router
  8. Side-effects
  9. URL history
  10. Quest list
  11. Hot reloading
  12. Clean up webpack configuration
  13. Better side-effects example
  14. Server tests
  15. package.json
  16. List of dependencies

Usage

git clone git@github.com:salsita/redux-boilerplate.git
cd redux-boilerplate
npm install
npm start

Navigate your browser to http://localhost:3000/

Development

npm run start:dev

Navigate your browser to http://localhost:3001/

Using immutable for redux store state

Immutable object is used for the store instead of a plain JavaScript object. This is against the redux specification. Because of this the combineReducers function from redux-side-effects can not be used out of the box.

The use of combineReducers is inevitable for instance for the use of redux-form in the project.

Either the developer writes their own combineReducers function (like the hive project does) or use the provided combineReducers function and relax the constraints from for instance this:

import { fromJS } from 'immutable';
export default fromJS({
  appState: {
    history: null
  },
  effects: []
});

to this:

import { fromJS } from 'immutable';
export default {
  main: fromJS({
    appState: {
      history: null
    },
    effects: []
  })
};
Passing environment variables to client

This is an example from the hive project on how an environment variable is passed to the client (browser-side) code. https://github.com/salsita/hive/blob/develop/web/webpack/webpack.frontend.config.js

const plugins = [
  new webpack.DefinePlugin({
    "process.env": {
      HIVE_LOGIN_PAGE: JSON.stringify(process.env.HIVE_LOGIN_PAGE),
      HIVE_LOGOUT_PAGE: JSON.stringify(process.env.HIVE_LOGOUT_PAGE)
    }
  })
];
...
module.exports = {
  ...
  plugins: plugins
};

On the client side, the variables are then accessed the same way as on the server side:

process.env.HIVE_LOGIN_PAGE;
process.env.HIVE_LOGOUT_PAGE;

Quest list

  • autoprefixer-loader is deprecated. postcss-loader should be used instead.
  • In npm run build-artefacts, the including of the node_modules directory should not be necessary because webpack tracks dependencies via require() statements and it is therefore suppossed to deliver them.
  • In router src/client/Routes.jsx, the default action should be to redirect to the root path (/) instead of displaying the not found page (via the NotFound component. Either check if the router allows it, or perhaps implement the component to dispatch an action (at some point) that will redirect to the root path.
Hot reloading

Hot reloading is not working properly (or at all). It may have to do with the use of devRunner.js, and maybe that the client does not connect back to the webpack-dev-server to listen for refresh events due to not being passed the port number.

Also react-hot-loader is going to be phased out so react-transform-hmr should be used instead. For instance the book SurviveJS - Webpack and React presents a working example.

Clean up webpack configuration

There are currently four webpack configuration files which is way to many. There is a separate configuration file for frontend (the client), the backend (the server), and the development and production environment.

The webpack.backend.config.js and webpack.frontend.config.js are utilized by webpack which is started by the babel-node in the start:dev package.json command (done via devRunner.js).

There should be a common file for both backend and frontend to get rid of the duplicate configuration. Then instead of having four webpack files, use the process.env.npm_lifecycle_event to make selection between a production and a development build.

For instance use this in webpack.conf:

var webpack = require('webpack');
var merge = require('webpack-merge');
const TARGET = process.env.npm_lifecycle_event;
const common = { // shared configuration for production and development builds };
// ...
if (TARGET === 'start') {
  module.exports = merge(common, { // production but not development configuration });  
}
if (TARGET === 'start:dev') {
  module.exports = merge(common, { // development but not production configuration });
}

Where the start and start:dev commands come from package.json

{
  "scripts": {
    "start":     // command to start the production build
    "start:dev": // command start the development build
  }
}

For example it usefull to have the webpack configuration to enable source maps for the development builds but not for the production builds. And also to have the production build source code minified (see -d and -p options) to decrease the network traffic and the browser application load time.

Better side-effects example

Better side-effects example is needed. Something to show handling of success and failure using store reducers when exchanging data with the server.

Server source code

The server src/server/main.js source code listens for http requests on port 3000, or port number configured by the environment variable PORT. It serves static files from the dist/client directory under url path /. It returns JSON data for http get request on path /hello. For every other url path, the server returns the default index.html page.

Client source code

Application URL router

The client router src/client/Router.jsx shows which React component implements which url path. For instance when the user types into a browser a url path that the server does not know and replies with the default index.html file content, the client NotFound React component will render the 404 Page Not Found in the browser.

Side-effects

This a simple example of a side effect from src/client/reducers/testingReducer.js. The side-effect here is the dispatch of the routerBootstrapped action.

export function* applicationMounting(state) {
  yield (dispatch) => {
    dispatch(TestingActions.routerBootstrapped(history));
  };

  return state;
}

export function* routerBootstrapped(state, _history) {
  return state.setIn(['appState', 'history'], _history);
}
URL history

Sometimes it is needed to be able to navigate to different url path from a current one. Regardless of how the url path is constructed, the pushState function of the history object can be used to instruct the browser to change to the given url path. The application router then gets to select the component to render the page based on the new url path. No request to the server is made.

Example from src/client/reducers/testingReducer.js:

import createBrowserHistory from 'history/lib/createBrowserHistory';
const history = createBrowserHistory();

export function* fooClicked(state) {
  history.pushState({}, '/foo');
  return state;
}

export function* barClicked(state) {
  history.pushState({}, '/bar');
  return state;
}

Favicon

Favicon did not always function properly. The trick that made the favicon to be picked up by the browser was to add the graphics in png format src/client/static-resources/favicon.png and to modify the src/client/static-resources/index.html to let the browser to choose from multiple formats. Like this:

<head>
  <link rel="icon" href="favicon.ico" type="image/x-icon" />
  <link rel="icon" href="favicon.png" type="image/png" /> ...
</head>

Server tests

There are two commands to run the same set of backend tests: npm run test_backend and npm run test_backend_cci. The reason is that mocha only allows for one test reporter. The test reporter nyan produces test reports readable by the user. The test reporter mocha-junit-reporter produces test reports for CircleCI (Circle Continuous Integration, hence the _cci suffix). The test reports are written to file test-results.xml.

package.json

If npm install command is to succeed on Windows (without cygwin), then the command must not use any syntax or programs specific to the Unix environment.

The npm run build-artifacts command produces production tarball for CircleCI which deploys it. Therefore it has to be named artifacts, not artefacts (interesting note).

Be prepared to do some configuration tinkering when placing shared source code outside the package.json directory, in order to make the eslint and babel to correctly work with these files.

List of dependencies

This is the list of dependencies taken from the package.json file with some short descriptions.

Development dependencies Synopsis
autoprefixer-loader Makes require('./file.css'); to compile and add the CSS to your page.
babel, babel-core Latest (ES2015 and beyond) JavaScript transpiler/compiler.
babel-eslint Allows to lint all valid Babel code with ESLlint.
babel-loader Allows transpiling JavaScript files using Babel and webpack.
css-loader CSS loader for webpack.
eslint Pluggable linting utility for JavaScript and JSX.
eslint-config-airbnb Airbnb JavaScript Style Guide.
eslint-loader ESLint loader for webpack
eslint-plugin-import ESLint plugin with support for linting of ES2015+ (ES6+) import/export syntax.
eslint-plugin-react React specific linting rules for ESLint.
file-loader var url = require("file!./file.png"); // => emits file.png as file in the output directory and returns the public url
font-awesome Scalable vector icons that can instantly be customized with CSS.
font-awesome-webpack Font awesome configuration and loading package for webpack, using font-awesome (Less).
karma Testing environment to make test-driven development easy.
karma-chai Make the Chai assertion library available in Karma.
karma-chrome-launcher x
karma-cli x
karma-junit-reporter x
karma-mocha x
karma-nyan-reporter Nyan Cat style test results reporter.
karma-phantomjs-launcher x
karma-webpack x
mocha JavaScript test framework for Node.js and the browser.
mocha-junit-reporter Produces JUnit-style XML test results.
phantomjs Scripted, headless browser used for automating web page interaction.
phantomjs-polyfill This is a polyfill for function.prototype.bind which is missing from PhantomJS.
raw-loader var fileContent = require("raw!./file.txt"); // => returns file.txt content as string
react-hot-loader Re-render the source code changes automatically in the browser.
request Simple way to make http calls with https and redirect support.
single-child Spawn a single child process which kills itself on restart.
sinon Standalone test spies, stubs and mocks for JavaScript.
sinon-chai Provide sinon for use with the Chai assertion library.
source-map-support Source map support for stack traces in node via the V8 stack trace API.
style-loader Style loader for webpack.
url-loader Url loader for webpack.
webpack Module bundler. The main purpose is to bundle JavaScript files for usage in a browser.
webpack-dev-server Serves a webpack application. Updates the browser on changes.
Dependencies Synopsis
babel-runtime Self-contained babel runtime.
bluebird JavaScript promise library.
express Web application framework for Node.js.
history JavaScript library to manage session history in browsers (and testing environments).
immutable Immutable collections for JavaScript. Immutable data cannot be changed once created.
invariant Throw exception if condition is false.
less, less-loader CSS pre-processor. Adds variables, mixins, functions and other techniques.
react JavaScript library for user interfaces.
react-document-meta HTML meta tags for React-based applications.
react-dom Entry point of the DOM-related rendering paths (ReactDOM.render()).
react-redux React bindings for Redux.
react-router Router directs URLs in a single page application to specific handlers.
redux Predictable state container for JavaScript applications.
redux-router Library to keep the router state (current pathname, query, and params) inside the Redux store.
redux-side-effects Redux store implementation with proper interface for asynchronous store operations.
serve-favicon Node.js middleware for serving a favicon.
serve-static Node.js middleware to serve static files from withing a given root directory.