├── app
├── src
│ ├── components
│ │ ├── category-row.jsx
│ │ ├── product-row.jsx
│ │ ├── product-table.jsx
│ │ └── search-bar.jsx
│ ├── models
│ │ └── products.jsx
│ └── filterable-product-table.jsx
├── stylesheets
│ └── main.css
└── main.jsx
├── .travis.yml
├── README.md
├── webpack.config.js
├── .gitignore
└── package.json
/app/src/components/category-row.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({category}) => (
4 |
5 | | {category} |
6 |
7 | );
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '5.3'
5 | - '6'
6 | matrix:
7 | allow_failures:
8 | - node_js: iojs
9 | - node_js: '0.12'
10 | - node_js: '6'
11 |
--------------------------------------------------------------------------------
/app/stylesheets/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #f0f0f0;
3 | color: #333;
4 | font-family: Helvetica;
5 | }
6 |
7 | input[type="text"] {
8 | margin-right: 1rem;
9 | }
10 |
11 | form {
12 | margin-bottom: 1rem;
13 | }
--------------------------------------------------------------------------------
/app/src/components/product-row.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({product}) => {
4 | var name = product.stocked ? product.name : {product.name} ;
5 | return (
6 |
7 | | {name} |
8 | {product.price} |
9 |
10 | );
11 | };
--------------------------------------------------------------------------------
/app/main.jsx:
--------------------------------------------------------------------------------
1 | import './stylesheets/main.css';
2 | import React from 'react';
3 | import {render} from 'react-dom';
4 | import FilterableProductTable from './src/filterable-product-table';
5 |
6 | // init shell
7 | initShell();
8 |
9 | function initShell() {
10 | var shell = document.createElement('main');
11 | shell.className = 'app-shell';
12 | document.body.appendChild(shell);
13 | render(, shell);
14 | }
--------------------------------------------------------------------------------
/app/src/models/products.jsx:
--------------------------------------------------------------------------------
1 | export default [
2 | {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
3 | {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
4 | {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
5 | {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
6 | {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
7 | {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
8 | ];
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React (15.6.0) example using ES2015 [](https://travis-ci.org/code0wl/react-example-modern-javascript)
2 |
3 | On the react site there is a demo on how to learn to think with react. This is the same tutorial executed using ES2015 and webpack as a build process. There are some gotcha's transforming your code to ES2015 whilst using React. In this demo there is a solution for these common challenges.
4 |
5 | Follow the original and great tutorial here
6 | [Es5 thinking in React](https://facebook.github.io/react/docs/thinking-in-react.html) and check the syntax differences using this project.
7 | [Changelog found here](https://facebook.github.io/react/blog/2016/11/16/react-v15.4.0.html)
8 |
9 | ```
10 | npm start
11 | ```
12 |
13 | Check the app at http://localhost:8080
14 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var ROOT_PATH = path.resolve(__dirname);
4 | var HtmlWebpackPlugin = require('html-webpack-plugin');
5 |
6 | module.exports = {
7 |
8 | entry: [path.resolve(ROOT_PATH, 'app/main.jsx')],
9 |
10 | resolve: {
11 | extensions: ['', '.js', '.jsx']
12 | },
13 |
14 | output: {
15 | path: path.resolve(ROOT_PATH, 'build'),
16 | filename: 'bundle.js'
17 | },
18 |
19 | plugins: [
20 | new HtmlWebpackPlugin({
21 | title: 'React ES2015'
22 | })
23 | ],
24 |
25 | module: {
26 | loaders: [
27 | {
28 | test: /\.jsx?$/,
29 | exclude: /node_modules/,
30 | loader: "babel",
31 | query: {
32 | presets: ['es2015', 'react']
33 | }
34 | },
35 |
36 | {
37 | test: /\.css$/,
38 | loaders: ['style', 'css']
39 | }
40 | ]
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | .DS_Store
4 | app/.DS_Store
5 | *.log
6 | .vscode
7 |
8 | ### JetBrains template
9 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
10 |
11 | *.iml
12 |
13 | ## Directory-based project format:
14 | .idea/
15 | # if you remove the above rule, at least ignore the following:
16 |
17 | # User-specific stuff:
18 | # .idea/workspace.xml
19 | # .idea/tasks.xml
20 | # .idea/dictionaries
21 |
22 | # Sensitive or high-churn files:
23 | # .idea/dataSources.ids
24 | # .idea/dataSources.xml
25 | # .idea/sqlDataSources.xml
26 | # .idea/dynamic.xml
27 | # .idea/uiDesigner.xml
28 |
29 | # Gradle:
30 | # .idea/gradle.xml
31 | # .idea/libraries
32 |
33 | # Mongo Explorer plugin:
34 | # .idea/mongoSettings.xml
35 |
36 | ## File-based project format:
37 | *.ipr
38 | *.iws
39 |
40 | ## Plugin-specific files:
41 |
42 | # IntelliJ
43 | /out/
44 |
45 | # mpeltonen/sbt-idea plugin
46 | .idea_modules/
47 |
48 | # JIRA plugin
49 | atlassian-ide-plugin.xml
50 |
51 | # Crashlytics plugin (for Android Studio and IntelliJ)
52 | com_crashlytics_export_strings.xml
53 | crashlytics.properties
54 | crashlytics-build.properties
55 |
--------------------------------------------------------------------------------
/app/src/components/product-table.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CategoryRow from './category-row';
3 | import ProductRow from './product-row';
4 |
5 | export default ({products, filterText, inStockOnly}) => {
6 |
7 | let rows = [], lastCategory = null;
8 |
9 | products.map((product) => {
10 | if (!product.name.toLowerCase().includes(filterText.toLowerCase()) || (!product.stocked && inStockOnly)) {
11 | return;
12 | }
13 |
14 | if (product.category !== lastCategory) {
15 | rows.push();
16 | }
17 |
18 | rows.push();
19 | lastCategory = product.category;
20 |
21 | });
22 |
23 | if (rows.length > 0) {
24 | return (
25 |
26 |
27 |
28 | | Name |
29 | Price |
30 |
31 |
32 | {rows}
33 |
34 | );
35 | } else {
36 | return \_(ツ)_/¯
;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/filterable-product-table.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import ProductTable from './components/product-table';
3 | import SearchBar from './components/search-bar';
4 | import products from './models/products';
5 |
6 | class FilterableProductTable extends Component {
7 |
8 | constructor() {
9 | super();
10 |
11 | this.handleUserInput = this.handleUserInput.bind(this);
12 |
13 | this.state = {
14 | filterText: '',
15 | inStockOnly: false
16 | }
17 | }
18 |
19 | handleUserInput(filterText, inStockOnly) {
20 | this.setState({
21 | filterText: filterText,
22 | inStockOnly: inStockOnly
23 | });
24 | }
25 |
26 | render() {
27 | return (
28 |
38 | )
39 | }
40 |
41 | }
42 |
43 | export default FilterableProductTable;
44 |
--------------------------------------------------------------------------------
/app/src/components/search-bar.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 |
3 | class SearchBar extends Component {
4 |
5 | constructor() {
6 | super();
7 | this.handleChange = this.handleChange.bind(this);
8 | }
9 |
10 | handleChange() {
11 | this.props.onUserInput(
12 | this.refs['filterTextInput'].value,
13 | this.refs['inStockOnlyInput'].checked
14 | );
15 | }
16 |
17 | render() {
18 | return (
19 |
38 | );
39 | }
40 | }
41 |
42 | export default SearchBar;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "modern-javascript-react",
3 | "version": "1.0.0",
4 | "description": "React es15 in best practice style.",
5 | "main": "webpack.config.js",
6 | "devDependencies": {
7 | "babel": "~6.5.2",
8 | "babel-cli": "~6.10.1",
9 | "babel-core": "~6.9.1",
10 | "babel-loader": "~6.2.4",
11 | "babel-preset-es2015": "~6.9.0",
12 | "babel-preset-react": "^6.5.0",
13 | "css-loader": "^0.23.1",
14 | "cssesc": "^0.1.0",
15 | "flatten": "1.0.2",
16 | "html-webpack-plugin": "^2.21.0",
17 | "http-server": "^0.9.0",
18 | "indexes-of": "^1.0.1",
19 | "node-libs-browser": "^1.0.0",
20 | "normalize": "^0.3.1",
21 | "style-loader": "^0.13.1",
22 | "webpack": "~1.13.1",
23 | "webpack-dev-server": "~1.14.1",
24 | "webpack-merge": "~0.14.0"
25 | },
26 | "private": false,
27 | "scripts": {
28 | "build": "webpack",
29 | "start": "npm i && webpack && node ./node_modules/http-server/bin/http-server ./build -p 8080 -o"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "git+https://github.com/Ositoozy/react-example-es2015.git"
34 | },
35 | "author": "Oscar Lodriguez",
36 | "license": "ISC",
37 | "bugs": {
38 | "url": "https://github.com/Ositoozy/react-example-es2015.git/issues"
39 | },
40 | "homepage": "https://github.com/Ositoozy/react-example-es2015.git#readme",
41 | "dependencies": {
42 | "react": "15.6.0",
43 | "react-dom": "15.6.0",
44 | "tapable": "^0.2.4"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------