Resources
webpack-dev-server
npm i webpack-dev-server -g
webpack-dev-server --config webpack.config --port 3000
ES 6 Destructuring
// module loading
const { Loader, main } = require('toolkit/loader');
// array destructuring
var foo = ["one", "two", "three"];
var [one, two, three] = foo;
Redux Flow
- Action
- Store
- Reducer
- Container component
Container component
- constructor function: initialise the state, bind functions for this
- child functions: called by render
- render function
- PropTypes validation
- Redux connect and related functions
props vs state
To build a static version of your app that renders your data model, you'll want to build components that reuse other components and pass data using props. props are a way of passing data from parent to child. If you're familiar with the concept of state, don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.
You can build top-down or bottom-up. That is, you can either start with building the components higher up in the hierarchy (i.e. starting with FilterableProductTable) or with the ones lower in it (ProductRow). In simpler examples, it's usually easier to go top-down, and on larger projects, it's easier to go bottom-up and write tests as you build.
At the end of this step, you'll have a library of reusable components that render your data model. The components will only have render() methods since this is a static version of your app. The component at the top of the hierarchy (FilterableProductTable) will take your data model as a prop. If you make a change to your underlying data model and call ReactDOM.render() again, the UI will be updated. It's easy to see how your UI is updated and where to make changes since there's nothing complicated going on. React's one-way data flow (also called one-way binding) keeps everything modular and fast.
Setting up Atom
- react
- terminal-plus
Dependencies
- babel-eslint
- eslint
- eslint-config-airbnb
- eslint-plugin-react
- react-native
- react-native-swipeout
- redux
- babel-node: Babel's second CLI which works exactly the same as Node.js' CLI, only it will compile ES6 code before running it. Not meant for production use
- npm-run-all: A CLI tool to run multiple npm-scripts in parallel or sequential
.eslint: https://gist.github.com/hendrikswan/2153ae77f1496d2b40aa
npm scripts
"scripts": {
"prestart": "babel-node tools/startMessage.js",
"start": "npm-run-all --parallel open:src lint:watch test:watch",
"open:src": "babel-node tools/srcServer.js",
"lint": "node_modules/.bin/esw webpack.config.* src tools",
"lint:watch": "npm run lint -- --watch",
"test": "mocha --reporter progress tools/testSetup.js \"src/**/*.test.js\"",
"test:watch": "npm run test -- --watch"
},
es linting
- add .eslintrc
- npm run lint
//package.json
"scripts": {
"prestart": "babel-node tools/startMessage.js",
"start": "babel-node tools/srcServer.js",
"lint": "node_modules/.bin/esw webpack.config.* src tools",
"lint:watch": "npm run lint -- --watch"
},
// .eslintrc
{
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"plugins": [
"react"
],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"browser": true,
"node": true,
"jquery": true,
"mocha": true
},
"rules": {
"quotes": 0,
"no-console": 1,
"no-debugger": 1,
"no-var": 1,
"semi": [1, "always"],
"no-trailing-spaces": 0,
"eol-last": 0,
"no-unused-vars": 0,
"no-underscore-dangle": 0,
"no-alert": 0,
"no-lone-blocks": 0,
"jsx-quotes": 1,
"react/display-name": [ 1, {"ignoreTranspilerName": false }],
"react/forbid-prop-types": [1, {"forbid": ["any"]}],
"react/jsx-boolean-value": 1,
"react/jsx-closing-bracket-location": 0,
"react/jsx-curly-spacing": 1,
"react/jsx-indent-props": 0,
"react/jsx-key": 1,
"react/jsx-max-props-per-line": 0,
"react/jsx-no-bind": 1,
"react/jsx-no-duplicate-props": 1,
"react/jsx-no-literals": 0,
"react/jsx-no-undef": 1,
"react/jsx-pascal-case": 1,
"react/jsx-sort-prop-types": 0,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-danger": 1,
"react/no-did-mount-set-state": 1,
"react/no-did-update-set-state": 1,
"react/no-direct-mutation-state": 1,
"react/no-multi-comp": 1,
"react/no-set-state": 0,
"react/no-unknown-property": 1,
"react/prefer-es6-class": 1,
"react/prop-types": 1,
"react/react-in-jsx-scope": 1,
"react/require-extension": 1,
"react/self-closing-comp": 1,
"react/sort-comp": 1,
"react/wrap-multilines": 1
}
}
test set up
- add testSetup.js to tools folder
//index.test.js
import expect from 'expect';
describe('Our first test', () => {
it('should pass', () => {
expect(true).toEqual(true);
});
});
//testSetup.js
// This file is written in ES5 since it's not transpiled by Babel.
// This file does the following:
// 1. Sets Node environment variable
// 2. Registers babel for transpiling our code for testing
// 3. Disables Webpack-specific features that Mocha doesn't understand.
// 4. Requires jsdom so we can test via an in-memory DOM in Node
// 5. Sets up global vars that mimic a browser.
/* eslint-disable no-var*/
/* This setting assures the .babelrc dev config (which includes
hot module reloading code) doesn't apply for tests.
But also, we don't want to set it to production here for
two reasons:
1. You won't see any PropType validation warnings when
code is running in prod mode.
2. Tests will not display detailed error messages
when running against production version code
*/
process.env.NODE_ENV = 'test';
// Register babel so that it will transpile ES6 to ES5
// before our tests run.
require('babel-register')();
// Disable webpack-specific features for tests since
// Mocha doesn't know what to do with them.
require.extensions['.css'] = function () {return null;};
require.extensions['.png'] = function () {return null;};
require.extensions['.jpg'] = function () {return null;};
// Configure JSDOM and set global variables
// to simulate a browser environment for tests.
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
documentRef = document; //eslint-disable-line no-undef
tools and libraries
- exponent js: https://exponentjs.com/
- ngrok: https://ngrok.com/
change class name
Directory Structure
node_module
-- src
---- components
------ about
------ home
---- App.js
---- styles
-- tools
.babelrc
.editorconfig
.eslintrc
.gitignore
package.json
webpack.config.dev.js
-- src
Bootstrapping
Routing
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
import HomePage from './components/home/HomePage';
import AboutPage from './components/about/AboutPage';
import CoursesPage from './components/course/CoursesPage';
export default (
<Route path="/" component={App}>
<IndexRoute component={HomePage} />
<Route path="courses" component={CoursesPage} />
<Route path="about" component={AboutPage} />
</Route>
);
App
import React, {PropTypes} from 'react';
import Header from './common/Header';
class App extends React.Component {
render() {
return (
<div className="container-fluid">
<Header />
{this.props.children}
</div>
);
}
}
App.propTypes = {
children: PropTypes.object.isRequired
};
export default App;
index.js
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import './styles/styles.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
render (
<Router history={browserHistory} routes={routes} />,
document.getElementById('app')
);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Admin</title>
</head>
<body>
<div id="app"></div>
<script src="/bundle.js"></script>
</body>
</html>
<span className={this.props.buttonIcon}></span>