03: Tooling: npm, Babel, Webpack, ES2015
function loadable(TheComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true };
}
componentDidMount() {
doSomethingAsync()
.then(() => this.setState({loading: false});
}
render() {
if (this.state.loading) {
return <p>Loading…</p>;
}
return <TheComponent someNewProp={…}/>;
}const ExampleComponent =
loadable(<SomeComponent/>);
ReactDOM.render(<ExampleComponent/>);// ES2015 class syntax
class Greeting extends React.Component {
render() {
return (
// JSX
<p>Hello, {this.props.name}</p>
);
}
}
// NB: JSX
var greeting = <Greeting name="Martin" />;
// Add the element to the DOM
ReactDOM.render(
greeting,
document.getElementById('container')
);ReactDOM.render((
<A>
<B /> {/* "Child" node: passed to A as props.children */}
</A>
), document.getElementById('container'));
class B extends React.Component {
render() {
return (
<div>
<h1>Heading</h1>
<p>Body text</p>
</div>
);
}
}
class A extends React.Component {
render() {
return (
<div style={{padding: '1em', border: 'solid black 1px'}}>
{this.props.children} {/* Render B */}
</div>
);
}
}General, reusable wrappers!
'use strict';
class Person {
constructor(name) {
this.name = name;
}
}
const liam = new Person('Liam');
console.log(liam);
index.js
➜ node index.js
Person { name: 'Liam' }Run the code with node <filename>
('use strict'; to allow classes for now)
package.json
{
"name": "timetracker-client-react",
"scripts": {
"build": "webpack --config webpack.config.prod.js",
"dev": "node -r babel/register dev-server.js"
},
"dependencies": {
"bootstrap": "^3.3.5",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-redux": "^4.0.0",
"redux": "^3.0.4",
"style-loader": "^0.13.0",
"superagent": "^1.4.0"
},
"devDependencies": {
"babel": "^5.8.34",
"babel-core": "^5.8.25",
"babel-loader": "^5.3.2",
"babel-plugin-react-transform": "^1.1.1",
"express": "^4.13.3",
"react-transform-catch-errors": "^1.0.0",
"react-transform-hmr": "^1.0.1",
"redbox-react": "^1.2.0",
"redux-devtools": "^2.1.5",
"webpack": "^1.12.6",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.5.0"
}
}
$ npm install
$ npm install --save react
$ npm install --save-dev babelLibs (modules)
@
./node_modules/
{
"name": "f03-npm-scripts",
"scripts": {
"build": "concat src/**/*.js > output/script.js"
}
}
➜ npm run build<!doctype html>
<html>
<head>
<title>f03!</title>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css"/>
<script src="node_modules/babel-core/browser.js"></script>
<script src="node_modules/react/dist/react.js"></script>
<script src="node_modules/react-dom/dist/react-dom.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/babel">
const text = 'blah';
ReactDOM.render((
<p>Hello </p>
), document.getElementById('container'));
</script>
</body>
</html>
{
"name": "f03-demo",
"dependencies": {
"babel": "^5.8.35",
"bootstrap": "^3.3.6",
"react": "^0.14.7",
"react-dom": "^0.14.7"
}
}
package.json
index.html
➜ babel script.jsx
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `This person is called ${ this.name }!`;
}
}
const martin = new Person("Martin");
console.log(martin);
No transforms
➜ babel script.jsx
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `This person is called ${ this.name }!`;
}
}
const martin = new Person("Martin");
console.log(martin);
No transforms
➜ babel script.jsx --plugins transform-es2015-block-scoping
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `This person is called ${ this.name }!`;
}
}
var martin = new Person("Martin");
console.log(martin);
block scoping transform
➜ babel script.jsx --presets es2015
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Person = function () {
function Person(name) {
_classCallCheck(this, Person);
this.name = name;
}
_createClass(Person, [{
key: "toString",
value: function toString() {
return "This person is called " + this.name + "!";
}
}]);
return Person;
}();
var martin = new Person("Martin");
console.log(martin);➜ babel script.jsx --presets es2015 > output/script.js<!doctype html>
<html>
<head>
<title>Webpack demo!</title>
</head>
<body>
<div id="container"></div>
<script src="bundle.js"></script>
</body>
</html>export default class Person {
constructor(name) {
this.name = name;
}
}
import Person from './Person';
const liam = new Person('Liam');
console.log(liam);{
"name": "webpack-demo",
"scripts": {
"build": "webpack --config webpack.config.js"
},
"devDependencies": {
"babel-loader": "^6.2.1",
"webpack": "^1.12.12"
}
}
const path = require('path');
const webpack = require('webpack');
const srcDir = path.join(__dirname, 'src');
module.exports = {
entry: path.join(srcDir, 'index.js'),
output: {
path: path.join(__dirname, 'output'),
filename: 'bundle.js',
},
module: {
loaders: [
{ test: /\.jsx?$/, loader: 'babel', include: srcDir },
],
},
};
index.html
src/Person.js
src/index.js
package.json
webpack.config.js