├── .gitignore ├── 01-hello-world ├── hello-world-1.html └── hello-world-2-with-DOM-attribute.html ├── 02-first-component-es5 └── first-component-es5.html ├── 03-first-component-es6-jsx ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ └── index.js ├── 04-es6-examples ├── arrow-functions-expression-bodies.js ├── arrow-functions-lexical-this.js ├── classes.js ├── const.js ├── default-parameter.js ├── destructuring-array-matching.js ├── destructuring-parameter-context-matching.js ├── let.js ├── module-import-export.js ├── spread-operator.js └── template-strings.js ├── 05-jsx-examples └── comparison-jsx-js.js ├── 06-component-composition ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ └── index.js ├── 07-component-props ├── .gitignore ├── README.md ├── package.json ├── proptype_validators.js ├── public │ └── index.html └── src │ └── index.js ├── 08-component-state ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ └── index.js ├── 09-component-lifecycle ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ ├── App.js │ ├── Counter.js │ └── index.js ├── 10-events ├── .gitignore ├── README.md ├── difference-html-react.js ├── package.json ├── public │ └── index.html └── src │ └── index.js ├── 11-keys ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ └── index.js ├── 12-redux ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ ├── actions │ ├── authorActions.js │ └── recipeActions.js │ ├── components │ ├── AddRecipe.js │ ├── App.js │ ├── ChangeAuthor.js │ ├── RecipeList.js │ └── RecipeListItem.js │ ├── index.js │ └── reducers │ ├── author.js │ ├── index.js │ └── recipes.js ├── 12b-data-fetching-redux ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ ├── actions │ ├── authorActions.js │ └── recipeActions.js │ ├── api │ ├── config.js │ └── db.json │ ├── components │ ├── AddRecipe.js │ ├── App.js │ ├── ChangeAuthor.js │ ├── RecipeList.js │ └── RecipeListItem.js │ ├── index.js │ └── reducers │ ├── author.js │ ├── index.js │ └── recipes.js ├── 13-react-router ├── .gitignore ├── README.md ├── package.json ├── public │ └── index.html └── src │ ├── components │ ├── App.js │ ├── Header.js │ ├── Home.js │ ├── RecipeDetail.js │ ├── RecipeOverview.js │ └── Recipes.js │ └── index.js ├── 14-react-build-process ├── .gitignore ├── README.md ├── config │ ├── env.js │ ├── jest │ │ ├── cssTransform.js │ │ └── fileTransform.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpackDevServer.config.js ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── scripts │ ├── build.js │ ├── start.js │ └── test.js └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js ├── 15-conference-application ├── index.html ├── step0-initial │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ └── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── img │ │ └── logo.svg │ │ ├── index.css │ │ └── index.js ├── step1-props │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ └── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── index.css │ │ └── index.js ├── step2-state │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── index.css │ │ └── index.js ├── step3-router │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── index.css │ │ └── index.js ├── step4-more-state-optional │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Favourites │ │ │ ├── Favourites.css │ │ │ └── Favourites.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── img │ │ └── star.png │ │ ├── index.css │ │ └── index.js ├── step5a-data-fetching-external-API │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── index.css │ │ └── index.js ├── step5b-data-fetching-external-API-with-favourites │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Favourites │ │ │ ├── Favourites.css │ │ │ └── Favourites.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── img │ │ └── star.png │ │ ├── index.css │ │ └── index.js ├── step5c-data-fetching-local-API │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── api │ │ ├── config.js │ │ └── db.json │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── index.css │ │ └── index.js ├── step6a-redux-external-API │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ ├── daysActions.js │ │ └── talksActions.js │ │ ├── api │ │ └── config.js │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── index.css │ │ ├── index.js │ │ └── reducers │ │ ├── days.js │ │ ├── index.js │ │ └── talks.js ├── step6b-redux-external-API-with-favourites │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ ├── daysActions.js │ │ ├── favouritesActions.js │ │ └── talksActions.js │ │ ├── api │ │ └── config.js │ │ ├── components │ │ ├── App │ │ │ ├── App.css │ │ │ └── App.js │ │ ├── Favourites │ │ │ ├── Favourites.css │ │ │ └── Favourites.js │ │ ├── Header │ │ │ ├── Header.css │ │ │ └── Header.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Navigation │ │ │ ├── Navigation.css │ │ │ └── Navigation.js │ │ ├── NavigationButton │ │ │ ├── NavigationButton.css │ │ │ └── NavigationButton.js │ │ ├── NotFound │ │ │ └── NotFound.js │ │ ├── TalkItem │ │ │ ├── TalkItem.css │ │ │ └── TalkItem.js │ │ └── TalkOverview │ │ │ ├── TalkOverview.css │ │ │ └── TalkOverview.js │ │ ├── img │ │ └── star.png │ │ ├── index.css │ │ ├── index.js │ │ └── reducers │ │ ├── days.js │ │ ├── favourites.js │ │ ├── index.js │ │ └── talks.js └── step6c-redux-local-API │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ └── index.html │ └── src │ ├── actions │ ├── daysActions.js │ └── talksActions.js │ ├── api │ ├── config.js │ └── db.json │ ├── components │ ├── App │ │ ├── App.css │ │ └── App.js │ ├── Header │ │ ├── Header.css │ │ └── Header.js │ ├── Home │ │ ├── Home.css │ │ └── Home.js │ ├── Navigation │ │ ├── Navigation.css │ │ └── Navigation.js │ ├── NavigationButton │ │ ├── NavigationButton.css │ │ └── NavigationButton.js │ ├── NotFound │ │ └── NotFound.js │ ├── TalkItem │ │ ├── TalkItem.css │ │ └── TalkItem.js │ └── TalkOverview │ │ ├── TalkOverview.css │ │ └── TalkOverview.js │ ├── index.css │ ├── index.js │ └── reducers │ ├── days.js │ ├── index.js │ └── talks.js ├── ConferenceApp_screenshot.png └── Readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode -------------------------------------------------------------------------------- /01-hello-world/hello-world-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 - Hello World 5 | 6 | 7 |
8 | 9 | 10 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /01-hello-world/hello-world-2-with-DOM-attribute.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 - Hello World 5 | 6 | 7 |
8 | 9 | 10 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /02-first-component-es5/first-component-es5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2 - First Component - ES5 5 | 6 | 7 |
8 | 9 | 10 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /03-first-component-es6-jsx/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /03-first-component-es6-jsx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "react": "^15.5.4", 10 | "react-dom": "^15.5.4" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /03-first-component-es6-jsx/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 3 - First Component - ES6 - JSX 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /03-first-component-es6-jsx/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | class MyComponent extends React.Component { 5 | render() { 6 | return ( 7 |

Hello First Component with ES6 & JSX!

8 | ); 9 | } 10 | } 11 | 12 | ReactDOM.render( 13 | , 14 | document.getElementById('mount') 15 | ); 16 | 17 | 18 | -------------------------------------------------------------------------------- /04-es6-examples/arrow-functions-expression-bodies.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#ExpressionBodies 2 | // in ES6 3 | odds = evens.map(v => v + 1); 4 | 5 | // in ES5 6 | odds = evens.map(function (v) { return v + 1; }); 7 | -------------------------------------------------------------------------------- /04-es6-examples/arrow-functions-lexical-this.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#Lexicalthis 2 | // in ES6 3 | this.nums.forEach((v) => { 4 | if (v % 5 === 0) 5 | this.fives.push(v); 6 | }) 7 | 8 | // in ES5 9 | // variant 1 10 | var self = this; 11 | this.nums.forEach(function (v) { 12 | if (v % 5 === 0) 13 | self.fives.push(v); 14 | }); 15 | 16 | // variant 2 17 | this.nums.forEach(function (v) { 18 | if (v % 5 === 0) 19 | this.fives.push(v); 20 | }, this); 21 | 22 | // variant 3 (since ECMAScript 5.1 only) 23 | this.nums.forEach(function (v) { 24 | if (v % 5 === 0) 25 | this.fives.push(v); 26 | }.bind(this)); 27 | 28 | -------------------------------------------------------------------------------- /04-es6-examples/classes.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#ClassDefinition 2 | // in ES6 3 | class Shape { 4 | constructor (id, x, y) { 5 | this.id = id; 6 | this.move(x, y); 7 | } 8 | move (x, y) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | } 13 | 14 | // in ES5 15 | var Shape = function (id, x, y) { 16 | this.id = id; 17 | this.move(x, y); 18 | }; 19 | Shape.prototype.move = function (x, y) { 20 | this.x = x; 21 | this.y = y; 22 | }; 23 | -------------------------------------------------------------------------------- /04-es6-examples/const.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#Constants 2 | // in ES6 3 | const PI = 3.141593; 4 | 5 | // in ES5 6 | // only through the help of object properties 7 | // and only in global context and not in a block scope 8 | Object.defineProperty(typeof global === "object" ? global : window, "PI", { 9 | value: 3.141593, 10 | enumerable: true, 11 | writable: false, 12 | configurable: false 13 | }) 14 | 15 | -------------------------------------------------------------------------------- /04-es6-examples/default-parameter.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#DefaultParameterValues 2 | // in ES6 3 | function f (x, y = 7, z = 42) { 4 | return x + y + z; 5 | } 6 | f(1) === 50; 7 | 8 | // in ES5 9 | function f (x, y, z) { 10 | if (y === undefined) 11 | y = 7; 12 | if (z === undefined) 13 | z = 42; 14 | return x + y + z; 15 | }; 16 | f(1) === 50; -------------------------------------------------------------------------------- /04-es6-examples/destructuring-array-matching.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#ArrayMatching 2 | // in ES6 3 | var list = [ 1, 2, 3 ]; 4 | var [ a, , b ] = list; 5 | 6 | // in ES5 7 | var list = [ 1, 2, 3 ]; 8 | var a = list[0]; 9 | var b = list[2]; 10 | -------------------------------------------------------------------------------- /04-es6-examples/destructuring-parameter-context-matching.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#ParameterContextMatching 2 | // in ES6 3 | function test ({ name, val }) { 4 | console.log(name, val); 5 | } 6 | test({ name: "bar", val: 42 }); 7 | 8 | // in ES5 9 | function test (arg) { 10 | var name = arg.name; 11 | var val = arg.val; 12 | console.log(name, val); 13 | }; 14 | test({ name: "bar", val: 42 }); 15 | 16 | -------------------------------------------------------------------------------- /04-es6-examples/let.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#BlockScopedVariables 2 | // in ES6 3 | function letTest() { 4 | let x = 31; 5 | if (true) { 6 | let x = 71; // other variable 7 | console.log(x); // 71 8 | } 9 | console.log(x); // 31 10 | } 11 | 12 | // in ES5 13 | function varTest() { 14 | var x = 31; 15 | if (true) { 16 | var x = 71; // same variable! 17 | console.log(x); // 71 18 | } 19 | console.log(x); // 71 20 | } 21 | 22 | -------------------------------------------------------------------------------- /04-es6-examples/module-import-export.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#ValueExportImport 2 | // in ES6 3 | // lib/math.js 4 | export function sum (x, y) { return x + y }; 5 | export var pi = 3.141593; 6 | 7 | // someApp.js 8 | import * as math from "lib/math"; 9 | console.log("2π = " + math.sum(math.pi, math.pi)); 10 | 11 | // otherApp.js 12 | import { sum, pi } from "lib/math"; 13 | console.log("2π = " + sum(pi, pi)); 14 | 15 | // in ES5 16 | // lib/math.js 17 | LibMath = {}; 18 | LibMath.sum = function (x, y) { return x + y }; 19 | LibMath.pi = 3.141593; 20 | 21 | // someApp.js 22 | var math = LibMath; 23 | console.log("2π = " + math.sum(math.pi, math.pi)); 24 | 25 | // otherApp.js 26 | var sum = LibMath.sum, pi = LibMath.pi; 27 | console.log("2π = " + sum(pi, pi)); 28 | 29 | -------------------------------------------------------------------------------- /04-es6-examples/spread-operator.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#SpreadOperator 2 | // in ES6 3 | var params = [ "hello", true, 7 ]; 4 | var other = [ 1, 2, ...params ]; // [ 1, 2, "hello", true, 7 ] 5 | 6 | // in ES5 7 | var params = [ "hello", true, 7 ]; 8 | var other = [ 1, 2 ].concat(params); // [ 1, 2, "hello", true, 7 ] 9 | -------------------------------------------------------------------------------- /04-es6-examples/template-strings.js: -------------------------------------------------------------------------------- 1 | // Full Reference: http://es6-features.org/#StringInterpolation 2 | // in ES6 3 | var user = { name: 'Tom' }; 4 | var myString = `Hello ${user.name}!`; 5 | 6 | // in ES5 7 | var user = { name: `Tom` }; 8 | var myString = "Hello " + user.name + "!"; 9 | 10 | -------------------------------------------------------------------------------- /05-jsx-examples/comparison-jsx-js.js: -------------------------------------------------------------------------------- 1 | // Built-in component 2 | // JSX code 3 |

Hello World!

4 | 5 | // compiled JS code 6 | React.createElement(h1, {id: 'myTitle'}, 'Hello World!'); 7 | 8 | 9 | 10 | // User-Defined component 11 | // JSX code 12 | Click Me 13 | 14 | // compiled JS code 15 | React.createElement(MyButton, {size: 'big'}, 'Click Me'); 16 | 17 | -------------------------------------------------------------------------------- /06-component-composition/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /06-component-composition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "react": "^15.5.4", 10 | "react-dom": "^15.5.4" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /06-component-composition/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 6 - Component Composition 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /06-component-composition/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | class UserDetails extends React.Component { 5 | render(){ 6 | return ( 7 |
8 |

User Details

9 |

Name: Tim

10 |
11 | ); 12 | } 13 | } 14 | 15 | class UserList extends React.Component { 16 | render() { 17 | return ( 18 |
19 | 20 | 21 |
22 | ); 23 | } 24 | } 25 | 26 | ReactDOM.render(, document.getElementById('mount')); 27 | 28 | 29 | -------------------------------------------------------------------------------- /07-component-props/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /07-component-props/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.5.8", 10 | "react": "^15.5.4", 11 | "react-dom": "^15.5.4" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /07-component-props/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 - Component Props 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /07-component-props/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import PropTypes from 'prop-types'; 4 | 5 | class UserDetails extends React.Component { 6 | render(){ 7 | return ( 8 |
9 |

User Details

10 |

Name: {this.props.name}

11 |
12 | ); 13 | } 14 | } 15 | UserDetails.defaultProps = { 16 | name: "Mike" 17 | }; 18 | UserDetails.propTypes = { 19 | name: PropTypes.string 20 | }; 21 | 22 | class UserList extends React.Component { 23 | render() { 24 | return ( 25 |
26 | 27 | 28 |
29 | ); 30 | } 31 | } 32 | 33 | 34 | ReactDOM.render(, document.getElementById('mount')); 35 | 36 | 37 | -------------------------------------------------------------------------------- /08-component-state/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /08-component-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.5.8", 10 | "react": "^15.5.4", 11 | "react-dom": "^15.5.4" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /08-component-state/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 - Component State 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /08-component-state/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | // stateless component 5 | const CounterText = ({ count }) => { 6 | return (

Counter: {count}

) 7 | } 8 | 9 | // stateful component 10 | // class CounterText extends React.Component { 11 | // render(){ 12 | // return (

Counter: {this.props.count}

); 13 | // } 14 | // } 15 | 16 | class Counter extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | counter: 0 21 | }; 22 | } 23 | updateCounter() { 24 | this.setState((prevState, props) => ({ 25 | counter: prevState.counter + 1 26 | })); 27 | } 28 | render() { 29 | return ( 30 |
31 | 32 | 35 |
36 | ); 37 | } 38 | } 39 | 40 | ReactDOM.render(, document.getElementById('mount')); 41 | 42 | 43 | -------------------------------------------------------------------------------- /09-component-lifecycle/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /09-component-lifecycle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.5.8", 10 | "react": "^15.5.4", 11 | "react-dom": "^15.5.4" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /09-component-lifecycle/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 - Component Lifecycle 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /09-component-lifecycle/src/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const color = 'color: green'; 4 | 5 | class Counter extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | console.log('%cCounter - constructor()', color); 9 | this.state = { 10 | counter: 0 11 | }; 12 | } 13 | componentWillMount(){ 14 | console.log('%cCounter - componentWillMount()', color); 15 | } 16 | componentDidMount(){ 17 | console.log('%cCounter - componentDidMount()', color); 18 | } 19 | componentWillReceiveProps(nextProps){ 20 | console.log('%cCounter - componentWillReceiveProps()', color); 21 | } 22 | shouldComponentUpdate(nextProps, nextState){ 23 | console.log('%cCounter - shouldComponentUpdate()', color); 24 | return true; 25 | } 26 | componentWillUpdate(){ 27 | console.log('%cCounter - componentWillUpdate()', color); 28 | } 29 | componentDidUpdate(){ 30 | console.log('%cCounter - componentDidUpdate()', color); 31 | } 32 | componentWillUnmount(){ 33 | console.log('%cCounter - componentWillUnmount()', color); 34 | } 35 | updateCounter() { 36 | console.log('%c--- Set state within Counter Component ---', color); 37 | this.setState((prevState, props) => ({ 38 | counter: prevState.counter + this.props.counterOperation 39 | })); 40 | } 41 | render() { 42 | console.log('%cCounter - render()', color); 43 | return ( 44 |
45 |

Counter: {this.state.counter}

46 |

Operation: { this.props.counterOperation === -1 ? 'Decrement' : 'Increment' }

47 | 48 |
49 | ); 50 | } 51 | } 52 | 53 | export default Counter; -------------------------------------------------------------------------------- /09-component-lifecycle/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('mount')); 6 | 7 | 8 | -------------------------------------------------------------------------------- /10-events/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /10-events/difference-html-react.js: -------------------------------------------------------------------------------- 1 | // Reference: https://facebook.github.io/react/docs/handling-events.html 2 | // HTML 3 | 6 | 7 | 8 | // React 9 | 12 | -------------------------------------------------------------------------------- /10-events/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.5.8", 10 | "react": "^15.5.4", 11 | "react-dom": "^15.5.4" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /10-events/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 - Events 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /10-events/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | class App extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | counter: 0, 9 | name: 'Tim' 10 | }; 11 | this.updateCounter = this.updateCounter.bind(this); 12 | } 13 | // this binding in constructor 14 | updateCounter(e) { 15 | let factor = 1; 16 | if (e.shiftKey) { 17 | factor = 10; 18 | } 19 | this.setState((prevState, props) => ({ 20 | counter: prevState.counter + factor 21 | })); 22 | } 23 | // this binding with arrow function 24 | updateInput(e){ 25 | this.setState({ name: e.target.value }); 26 | } 27 | render() { 28 | return ( 29 |
30 |
31 |

Counter: {this.state.counter}

32 | 35 |
36 |
37 |
38 |

Name: {this.state.name}

39 | this.updateInput(e)} /> 42 |
43 |
44 | ); 45 | } 46 | } 47 | 48 | ReactDOM.render(, document.getElementById('mount')); 49 | 50 | 51 | -------------------------------------------------------------------------------- /11-keys/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /11-keys/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.5.8", 10 | "react": "^15.5.4", 11 | "react-dom": "^15.5.4" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11-keys/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 - Keys 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /11-keys/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | class App extends React.Component { 5 | render() { 6 | const data = [ 7 | { id: 'A1', text: 'My awesome item'}, 8 | { id: 'B7', text: 'My premium item'}, 9 | { id: 'C9', text: 'My wonderful item'} 10 | ]; 11 | const listItems = data.map((item) => 12 |
  • 13 |

    {item.text}

    14 |
  • 15 | ); 16 | return ( 17 |
      18 | {listItems} 19 |
    20 | ); 21 | } 22 | } 23 | 24 | ReactDOM.render(, document.getElementById('mount')); 25 | 26 | 27 | -------------------------------------------------------------------------------- /12-redux/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /12-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.5.8", 10 | "react": "^15.5.4", 11 | "react-dom": "^15.5.4", 12 | "react-redux": "^5.0.5", 13 | "redux": "^3.6.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test --env=jsdom", 19 | "eject": "react-scripts eject" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12-redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12a - Redux Data Fetching 7 | 8 | 9 |
    10 | 11 | 12 | -------------------------------------------------------------------------------- /12-redux/src/actions/authorActions.js: -------------------------------------------------------------------------------- 1 | export const updateAuthor = (name) => { 2 | return { 3 | type: 'UPDATE_AUTHOR', 4 | payload: name 5 | } 6 | } -------------------------------------------------------------------------------- /12-redux/src/actions/recipeActions.js: -------------------------------------------------------------------------------- 1 | export const addRecipe = (recipe) => { 2 | return { 3 | type: 'ADD_RECIPE', 4 | payload: recipe 5 | } 6 | } -------------------------------------------------------------------------------- /12-redux/src/components/AddRecipe.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { addRecipe } from '../actions/recipeActions'; 4 | 5 | class AddRecipe extends React.Component{ 6 | constructor(props){ 7 | super(props); 8 | this.state = { 9 | name: '', 10 | time: '' 11 | } 12 | this.saveRecipe = this.saveRecipe.bind(this); 13 | } 14 | saveRecipe(){ 15 | this.props.saveRecipeToStore(this.state); 16 | this.setState({ name: '', time: '' }); 17 | } 18 | render(){ 19 | return ( 20 |
    21 |

    Add a new Recipe

    22 |
    23 | 24 | this.setState({ name: e.target.value })} /> 25 |
    26 |
    27 | 28 | this.setState({ time: e.target.value })} /> 29 |
    30 | 31 |
    32 | ); 33 | } 34 | } 35 | 36 | const mapDispatchToProps = (dispatch) => { 37 | return { 38 | saveRecipeToStore: (recipe) => dispatch(addRecipe(recipe)) 39 | } 40 | } 41 | 42 | export default connect(null, mapDispatchToProps)(AddRecipe) -------------------------------------------------------------------------------- /12-redux/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RecipeList from './RecipeList'; 3 | import AddRecipe from './AddRecipe'; 4 | import ChangeAuthor from './ChangeAuthor'; 5 | 6 | const App = () => { 7 | return ( 8 |
    9 |

    Recipe Book

    10 | 11 | 12 | 13 |
    14 | ); 15 | } 16 | 17 | export default App; -------------------------------------------------------------------------------- /12-redux/src/components/ChangeAuthor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { updateAuthor } from '../actions/authorActions'; 4 | 5 | class AddRecipe extends React.Component{ 6 | constructor(props){ 7 | super(props); 8 | this.state = { 9 | name: this.props.authorName, 10 | } 11 | this.changeAuthor = this.changeAuthor.bind(this); 12 | } 13 | componentWillReceiveProps(newProps) { 14 | if(this.props.authorName !== newProps.authorName) { 15 | this.setState({ name: newProps.authorName }); 16 | } 17 | } 18 | changeAuthor(){ 19 | this.props.saveAuthorToStore(this.state.name); 20 | this.setState({ name: '' }); 21 | } 22 | render(){ 23 | return ( 24 |
    25 |

    Change Author

    26 |
    27 | 28 | this.setState({ name: e.target.value })} /> 29 |
    30 | 31 |
    32 | ); 33 | } 34 | } 35 | const mapStateToProps = (state) => { 36 | return { 37 | authorName: state.author.name 38 | } 39 | } 40 | 41 | const mapDispatchToProps = (dispatch) => { 42 | return { 43 | saveAuthorToStore: (name) => dispatch(updateAuthor(name)) 44 | } 45 | } 46 | 47 | export default connect(mapStateToProps, mapDispatchToProps)(AddRecipe) -------------------------------------------------------------------------------- /12-redux/src/components/RecipeList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RecipeListItem from './RecipeListItem'; 3 | import { connect } from 'react-redux'; 4 | 5 | class RecipeList extends React.Component{ 6 | render(){ 7 | const list = this.props.recipeList.map((recipe, index) => { 8 | return ( 9 |
  • 10 | 11 |
  • 12 | ); 13 | }); 14 | return ( 15 |
    16 |

    By { this.props.authorName }

    17 |
      {list}
    18 |
    19 | ); 20 | } 21 | } 22 | 23 | const mapStateToProps = (state, ownProps) => { 24 | return { 25 | recipeList: state.recipes.list, 26 | authorName: state.author.name 27 | } 28 | } 29 | 30 | export default connect(mapStateToProps, null)(RecipeList) -------------------------------------------------------------------------------- /12-redux/src/components/RecipeListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const RecipeListItem = ({ name, time}) => { 4 | return (

    {name} ({time})

    ); 5 | } 6 | 7 | export default RecipeListItem; -------------------------------------------------------------------------------- /12-redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | import { Provider } from 'react-redux' 6 | import { createStore, compose } from 'redux' 7 | import reducers from './reducers'; 8 | 9 | 10 | // create store and use redux dev tool middleware as browser extension 11 | const store = createStore( 12 | reducers, 13 | compose(window.devToolsExtension ? window.devToolsExtension() : f => f) 14 | ) 15 | // or create store without dev tools 16 | // let store = createStore(reducers); 17 | 18 | ReactDOM.render( 19 | 20 | 21 | , 22 | document.getElementById('mount') 23 | ); 24 | 25 | 26 | -------------------------------------------------------------------------------- /12-redux/src/reducers/author.js: -------------------------------------------------------------------------------- 1 | // author reducer 2 | 3 | const initialState = { 4 | name: 'Kristin' 5 | } 6 | 7 | export default (state = initialState, action) => { 8 | switch (action.type) { 9 | case 'UPDATE_AUTHOR': { 10 | return { ...state, name: action.payload }; 11 | } 12 | default: 13 | return state; 14 | } 15 | }; -------------------------------------------------------------------------------- /12-redux/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import recipes from './recipes'; 3 | import author from './author'; 4 | 5 | const reducers = combineReducers({ 6 | recipes, 7 | author 8 | }) 9 | 10 | export default reducers; -------------------------------------------------------------------------------- /12-redux/src/reducers/recipes.js: -------------------------------------------------------------------------------- 1 | // recipe reducer 2 | 3 | const initialState = { 4 | list: [ 5 | { 6 | name: 'Hackbraten', 7 | time: '1 Stunde', 8 | } 9 | ] 10 | } 11 | 12 | export default (state = initialState, action) => { 13 | switch (action.type) { 14 | case 'ADD_RECIPE': 15 | return { ...state, list: [ ...state.list, action.payload] }; 16 | default: 17 | return state; 18 | } 19 | }; -------------------------------------------------------------------------------- /12b-data-fetching-redux/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /12b-data-fetching-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-first-component-es6-jsx", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "json-server": "^0.10.1", 10 | "prop-types": "^15.5.8", 11 | "react": "^15.5.4", 12 | "react-dom": "^15.5.4", 13 | "react-redux": "^5.0.5", 14 | "redux": "^3.6.0", 15 | "redux-thunk": "^2.2.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom", 21 | "eject": "react-scripts eject", 22 | "api": "json-server --watch src/api/db.json --port 3004" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /12b-data-fetching-redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 - Redux 7 | 8 | 9 |
    10 | 11 | 12 | -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/actions/authorActions.js: -------------------------------------------------------------------------------- 1 | import { config } from '../api/config'; 2 | 3 | export const writeAuthor = (name) => { 4 | return { 5 | type: 'UPDATE_AUTHOR', 6 | payload: name 7 | } 8 | } 9 | 10 | export const saveAuthor = (data) => { 11 | return dispatch => { 12 | return fetch(`${config.API_URL}/author`, { 13 | method: 'PUT', 14 | headers: { 15 | 'Accept': 'application/json', 16 | 'Content-Type': 'application/json' 17 | }, 18 | body: JSON.stringify({ 19 | "name": data, 20 | }) 21 | }) 22 | .then(response => response.json()) 23 | .then(json => dispatch(writeAuthor(data))) 24 | } 25 | } 26 | 27 | export const requestAuthor = () => { 28 | return { 29 | type: 'REQUEST_AUTHOR', 30 | payload: { 31 | loading: true 32 | } 33 | } 34 | } 35 | 36 | export const receiveAuthor = (data) => { 37 | return { 38 | type: 'RECEIVE_AUTHOR', 39 | payload: { 40 | data: data.name, 41 | loading: false 42 | } 43 | } 44 | } 45 | 46 | export const fetchAuthor = () => { 47 | return dispatch => { 48 | // set loading state 49 | dispatch(requestAuthor()); 50 | // fetch data from API 51 | return fetch(`${config.API_URL}/author`) 52 | .then(response => response.json()) 53 | .then(json => dispatch(receiveAuthor(json))) 54 | .catch((err) => console.error(err)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/actions/recipeActions.js: -------------------------------------------------------------------------------- 1 | import { config } from '../api/config'; 2 | 3 | export const addRecipe = (recipe) => { 4 | return { 5 | type: 'ADD_RECIPE', 6 | payload: recipe 7 | } 8 | } 9 | 10 | export const saveRecipe = (newRecipe) => { 11 | return (dispatch, getState) => { 12 | return fetch(`${config.API_URL}/recipes`, { 13 | method: 'PUT', 14 | headers: { 15 | 'Accept': 'application/json', 16 | 'Content-Type': 'application/json' 17 | }, 18 | body: JSON.stringify({ 19 | "list": [...getState().recipes.list, newRecipe], 20 | }) 21 | }) 22 | .then(response => response.json()) 23 | .then(json => dispatch(addRecipe(newRecipe))) 24 | } 25 | } 26 | 27 | export const requestRecipes = () => { 28 | return { 29 | type: 'REQUEST_RECIPES', 30 | payload: { 31 | loading: true 32 | } 33 | } 34 | } 35 | 36 | export const receiveRecipes = (data) => { 37 | return { 38 | type: 'RECEIVE_RECIPES', 39 | payload: { 40 | data: data.list, 41 | loading: false 42 | } 43 | } 44 | } 45 | 46 | export const fetchRecipes = () => { 47 | return dispatch => { 48 | // set loading state 49 | dispatch(requestRecipes()); 50 | // fetch data from API 51 | return fetch(`${config.API_URL}/recipes`) 52 | .then(response => response.json()) 53 | .then(json => dispatch(receiveRecipes(json))) 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/api/config.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | API_URL: 'http://localhost:3004', 3 | } -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/api/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "recipes": { 3 | "list": [ 4 | { 5 | "name": "Hack", 6 | "time": "1 Stunde" 7 | }, 8 | { 9 | "name": "Nudelsalat", 10 | "time": "20 Minuten" 11 | } 12 | ] 13 | }, 14 | "author": { 15 | "name": "Kristin" 16 | } 17 | } -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/components/AddRecipe.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { saveRecipe } from '../actions/recipeActions'; 4 | 5 | class AddRecipe extends React.Component{ 6 | constructor(props){ 7 | super(props); 8 | this.state = { 9 | name: '', 10 | time: '' 11 | } 12 | this.saveRecipe = this.saveRecipe.bind(this); 13 | } 14 | saveRecipe(){ 15 | this.props.saveRecipe(this.state); 16 | this.setState({ name: '', time: '' }); 17 | } 18 | render(){ 19 | return ( 20 |
    21 |

    Add a new Recipe

    22 |
    23 | 24 | this.setState({ name: e.target.value })} /> 25 |
    26 |
    27 | 28 | this.setState({ time: e.target.value })} /> 29 |
    30 | 31 |
    32 | ); 33 | } 34 | } 35 | 36 | const mapDispatchToProps = (dispatch) => { 37 | return { 38 | saveRecipe: (newRecipe) => dispatch(saveRecipe(newRecipe)) 39 | } 40 | } 41 | 42 | export default connect(null, mapDispatchToProps)(AddRecipe) -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RecipeList from './RecipeList'; 3 | import AddRecipe from './AddRecipe'; 4 | import ChangeAuthor from './ChangeAuthor'; 5 | 6 | const App = () => { 7 | return ( 8 |
    9 |

    Recipe Book

    10 | 11 | 12 | 13 |
    14 | ); 15 | } 16 | 17 | export default App; -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/components/ChangeAuthor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { saveAuthor } from '../actions/authorActions'; 4 | 5 | class AddRecipe extends React.Component{ 6 | constructor(props){ 7 | super(props); 8 | this.state = { 9 | name: this.props.authorName, 10 | } 11 | this.changeAuthor = this.changeAuthor.bind(this); 12 | } 13 | componentWillReceiveProps(newProps) { 14 | if(this.props.authorName !== newProps.authorName) { 15 | this.setState({ name: newProps.authorName }); 16 | } 17 | } 18 | changeAuthor(){ 19 | this.props.saveAuthor(this.state.name); 20 | this.setState({ name: '' }); 21 | } 22 | render(){ 23 | return ( 24 |
    25 |

    Change Author

    26 |
    27 | 28 | this.setState({ name: e.target.value })} /> 29 |
    30 | 31 |
    32 | ); 33 | } 34 | } 35 | const mapStateToProps = (state) => { 36 | return { 37 | authorName: state.author.name 38 | } 39 | } 40 | 41 | const mapDispatchToProps = (dispatch) => { 42 | return { 43 | saveAuthor: (data) => dispatch(saveAuthor(data)), 44 | } 45 | } 46 | 47 | export default connect(mapStateToProps, mapDispatchToProps)(AddRecipe) -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/components/RecipeList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RecipeListItem from './RecipeListItem'; 3 | import { connect } from 'react-redux'; 4 | import { fetchRecipes } from '../actions/recipeActions'; 5 | import { fetchAuthor } from '../actions/authorActions'; 6 | 7 | 8 | class RecipeList extends React.Component{ 9 | componentDidMount(){ 10 | this.props.fetchAllRecipes(); 11 | this.props.fetchAuthor(); 12 | } 13 | 14 | renderRecipes(){ 15 | const { recipeLoading, recipeList } = this.props; 16 | if(recipeLoading){ 17 | return (

    Loading recipes...

    ) 18 | } 19 | if(!recipeList || recipeList.length === 0){ 20 | return (

    No recipes loaded.

    ) 21 | } 22 | const list = recipeList.map((recipe, index) => { 23 | return ( 24 |
  • 25 | 26 |
  • 27 | ); 28 | }); 29 | return (
      {list}
    ); 30 | } 31 | 32 | renderAuthor(){ 33 | const { authorName } = this.props; 34 | if(!authorName) { 35 | return (

    Loading author...

    ) 36 | } 37 | return (

    By { authorName }

    ); 38 | } 39 | 40 | render(){ 41 | return ( 42 |
    43 | { this.renderAuthor() } 44 | { this.renderRecipes() } 45 |
    46 | ); 47 | } 48 | } 49 | 50 | const mapStateToProps = (state, ownProps) => { 51 | return { 52 | recipeList: state.recipes.list, 53 | recipeLoading: state.recipes.loading, 54 | authorName: state.author.name 55 | } 56 | } 57 | 58 | const mapDispatchToProps = (dispatch) => { 59 | return { 60 | fetchAllRecipes: () => dispatch(fetchRecipes()), 61 | fetchAuthor: () => dispatch(fetchAuthor()) 62 | } 63 | } 64 | 65 | export default connect(mapStateToProps, mapDispatchToProps)(RecipeList) -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/components/RecipeListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const RecipeListItem = ({ name, time}) => { 4 | return (

    {name} ({time})

    ); 5 | } 6 | 7 | export default RecipeListItem; -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | import { Provider } from 'react-redux' 6 | import { createStore, compose, applyMiddleware } from 'redux' 7 | import thunkMiddleware from 'redux-thunk'; 8 | import reducers from './reducers'; 9 | 10 | 11 | // create store and use redux dev tool middleware as browser extension 12 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 13 | const store = createStore( 14 | reducers, 15 | composeEnhancers( 16 | applyMiddleware(thunkMiddleware) 17 | ) 18 | ); 19 | 20 | ReactDOM.render( 21 | 22 | 23 | , 24 | document.getElementById('mount') 25 | ); 26 | 27 | 28 | -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/reducers/author.js: -------------------------------------------------------------------------------- 1 | // author reducer 2 | 3 | const initialState = { 4 | loading: false, 5 | name: null 6 | } 7 | 8 | export default (state = initialState, action) => { 9 | switch (action.type) { 10 | case 'UPDATE_AUTHOR': 11 | return { ...state, name: action.payload }; 12 | case 'REQUEST_AUTHOR': 13 | return { ...state, loading: action.payload.loading }; 14 | case 'RECEIVE_AUTHOR': 15 | return { ...state, loading: action.payload.loading, name: action.payload.data }; 16 | default: 17 | return state; 18 | } 19 | }; -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import recipes from './recipes'; 3 | import author from './author'; 4 | 5 | const reducers = combineReducers({ 6 | recipes, 7 | author 8 | }) 9 | 10 | export default reducers; -------------------------------------------------------------------------------- /12b-data-fetching-redux/src/reducers/recipes.js: -------------------------------------------------------------------------------- 1 | // recipe reducer 2 | 3 | const initialState = { 4 | loading: false, 5 | list: null 6 | } 7 | 8 | export default (state = initialState, action) => { 9 | switch (action.type) { 10 | case 'ADD_RECIPE': 11 | if(!state.list){ 12 | return { ...state, list: [ action.payload ] }; 13 | } 14 | return { ...state, list: [ ...state.list, action.payload] }; 15 | case 'REQUEST_RECIPES': 16 | return { ...state, loading: action.payload.loading }; 17 | case 'RECEIVE_RECIPES': 18 | return { ...state, loading: action.payload.loading, list: action.payload.data }; 19 | default: 20 | return state; 21 | } 22 | }; -------------------------------------------------------------------------------- /13-react-router/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /13-react-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "13-react-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4", 8 | "react-router-dom": "^4.1.1" 9 | }, 10 | "devDependencies": { 11 | "react-scripts": "1.0.7" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /13-react-router/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 - React Router 7 | 8 | 9 |
    10 | 11 | 12 | -------------------------------------------------------------------------------- /13-react-router/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Switch, Route } from 'react-router-dom'; 3 | import Home from './Home'; 4 | import Header from './Header'; 5 | import Recipes from './Recipes'; 6 | 7 | const App = () => ( 8 |
    9 |
    10 | 11 | 12 | 13 | 14 |
    15 | ); 16 | 17 | export default App; -------------------------------------------------------------------------------- /13-react-router/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const Header = () => { 5 | const tabData = [ 6 | { text: 'Home', link: '/' }, 7 | { text: 'Recipes', link: '/recipes' }, 8 | ]; 9 | const tabs = tabData.map((tab, index) => (
  • {tab.text}
  • )); 10 | return(
      {tabs}
    ); 11 | } 12 | 13 | export default Header; -------------------------------------------------------------------------------- /13-react-router/src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Home = () => ( 4 |

    Welcome to the Recipe Book as React Router Example

    5 | ) 6 | 7 | export default Home; -------------------------------------------------------------------------------- /13-react-router/src/components/RecipeDetail.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const recipeData = { 4 | '1' : { name: 'Nudelsalat', time: '1 Stunde'}, 5 | '2' : { name: 'Hackbraten', time: '30 Minuten'} 6 | } 7 | 8 | const RecipeDetail = ({ match, history}) => { 9 | const id = match.params.id 10 | const recipe = recipeData[id]; 11 | if(!recipe){ 12 | return (

    No recipe found!

    ); 13 | } 14 | return ( 15 |
    16 |

    Recipe with ID {id}

    17 |

    Name: {recipe.name}

    18 |

    Time: {recipe.time}

    19 | 20 |
    21 | ) 22 | } 23 | 24 | export default RecipeDetail; 25 | -------------------------------------------------------------------------------- /13-react-router/src/components/RecipeOverview.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | class RecipeOverview extends Component { 5 | render() { 6 | const recipeData = [ 7 | { id: 1, name: 'Nudelsalat', time: '1 Stunde'}, 8 | { id: 2, name: 'Hackbraten', time: '30 Minuten'} 9 | ]; 10 | const recipes = recipeData.map((recipe, index) => ( 11 |
  • {recipe.name} ({recipe.time})

  • 12 | )); 13 | return( 14 |
    15 |

    My Recipe List

    16 |
      {recipes}
    17 |
    18 | ) 19 | } 20 | } 21 | 22 | export default RecipeOverview; 23 | -------------------------------------------------------------------------------- /13-react-router/src/components/Recipes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Switch, Route } from 'react-router-dom'; 3 | import RecipeOverview from './RecipeOverview'; 4 | import RecipeDetail from './RecipeDetail'; 5 | 6 | const Recipes = () => ( 7 | 8 | 9 | 10 | 11 | ) 12 | 13 | export default Recipes; -------------------------------------------------------------------------------- /13-react-router/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import App from './components/App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('mount') 11 | ); 12 | 13 | -------------------------------------------------------------------------------- /14-react-build-process/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /14-react-build-process/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /14-react-build-process/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /14-react-build-process/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right