interaction-player
v5.8.8-SNAPSHOT
Published
Interaction Player
Downloads
2
Readme
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.
- Usage
- Development
- Using immutable for redux store state
- Passing environment variables to client
- Server source code
- Client source code
- Application URL router
- Side-effects
- URL history
- Quest list
- Hot reloading
- Clean up webpack configuration
- Better side-effects example
- Server tests
- package.json
- List of dependencies
Usage
git clone [email protected]: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 theNotFound
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.