-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy path010_react..011_react_app
1844 lines (1842 loc) · 113 KB
/
010_react..011_react_app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
diff --git a/.babelrc b/.babelrc
index 4ffef06..5e833aa 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,3 @@
{
- "presets": ["env", "react"]
+ "presets": ["env", "react", "stage-2"]
}
diff --git a/index.html b/index.html
index 5eab624..43bf50b 100644
--- a/index.html
+++ b/index.html
@@ -4,29 +4,13 @@
<meta charset="UTF-8">
<title>JavaScript Example</title>
<!-- link the generated css -->
- <link href="dist/common.css" rel="stylesheet" title="Default Style"/>
+ <link href="/dist/common.css" rel="stylesheet" title="Default Style"/>
</head>
<body>
- <h1>Hello from HTML!</h1>
- <div>Console.log here:</div>
- <div id="log"></div>
<div id="react"></div>
- <script>
- // https://stackoverflow.com/questions/20256760/javascript-console-log-to-html
- (function () {
- const logger = document.getElementById('log');
- console.log = function (message) {
- if (typeof message == 'object') {
- logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(message) : message) + '<br />';
- } else {
- logger.innerHTML += message + '<br />';
- }
- }
- })();
- </script>
<!-- generated the css we want to use AND mount React -->
- <script src="dist/vendor.js"></script>
- <script src="dist/common.js"></script>
- <script src="dist/index.js"></script>
+ <script src="/dist/vendor.js"></script>
+ <script src="/dist/common.js"></script>
+ <script src="/dist/index.js"></script>
</body>
</html>
diff --git a/package.json b/package.json
index 8d6eef2..42dddc2 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,9 @@
"description": "based on the article of https://medium.com/@peterxjang/modern-javascript-explained-for-dinosaurs-f695e9747b70",
"main": "index.js",
"scripts": {
- "bundle": "npm run lint && npm test && npm run build",
+ "build": "npm run lint && npm test && npm run bundle",
+ "bundle": "webpack --progress -p",
"bundle:watch": "webpack --progress --watch",
- "build": "webpack --progress -p",
"lint": "eslint src/js",
"test": "jest",
"test:watch": "jest --watch",
@@ -31,7 +31,8 @@
"dependencies": {
"moment": "2.19.1",
"react": "16.0.0",
- "react-dom": "16.0.0"
+ "react-dom": "16.0.0",
+ "react-router-dom": "4.2.2"
},
"devDependencies": {
"@babel/core": "^7.0.0-beta.4",
@@ -39,6 +40,7 @@
"babel-loader": "7.1.2",
"babel-preset-env": "1.6.1",
"babel-preset-react": "6.24.1",
+ "babel-preset-stage-2": "6.24.1",
"css-loader": "0.28.7",
"eslint": "4.10.0",
"eslint-loader": "1.9.0",
@@ -49,7 +51,9 @@
"jest-junit": "3.1.0",
"less": "2.7.3",
"less-loader": "4.0.5",
+ "prop-types": "15.6.0",
"style-loader": "0.19.0",
+ "svg-url-loader": "2.3.0",
"webpack": "3.8.1",
"webpack-dev-server": "2.9.3"
},
diff --git a/part2.md b/part2.md
index 1cb203a..1c121e0 100644
--- a/part2.md
+++ b/part2.md
@@ -473,4 +473,797 @@ file can be loaded once initially, and stored in cache for later use.
This results in pagespeed optimizations as the browser can quickly serve the shared
code from cache, rather than being forced to load a larger bundle whenever a new page is visited.
-Now that we have a nice performing integration of react let us create our first react app.
\ No newline at end of file
+
+## React App ([react](https://facebook.github.io/react/))
+
+**Branch: [react_app](https://github.com/scherler/Modern-JavaScript-Explained-For-Dinosaurs/tree/011_react_app)**
+
+**[Diff](./diffs/010_react..011_react_app)** `git diff 010_react..011_react_app`
+
+Now that we have a nice performing integration of react let us create our first react app.
+
+### Our first component
+
+We recommend the official [react tutorial](https://reactjs.org/tutorial/tutorial.html) to get a full overview of what you can do.
+
+Our first component is to extract `<div>Hello React!</div>` from
+
+```javascript
+ReactDOM.render(<div>Hello React!</div>, root);
+```
+
+We will create src/js/components/Hello.jsx with the following content:
+
+```javascript
+import React, { Component } from 'react';
+
+export class Hello extends Component {
+ render() {
+ return (<div>Hello React!</div>);
+ }
+}
+```
+
+and our `common.js` will become
+
+```javascript
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Hello } from './components/Hello';
+import '../less/index.less'; // tell webpack to request the transpiling of less to css
+
+const root = document.getElementById('react');
+ReactDOM.render(<Hello />, root);
+```
+
+However when you try bundle our app you will get an error like:
+
+```bash
+ERROR in ./src/js/common.js
+Module not found: Error: Can't resolve './components/Hello' in '/opt/src/mysterion/Modern-JavaScript-Explained-For-Dinosaurs/src/js'
+ @ ./src/js/common.js 11:13-42
+Child extract-text-webpack-plugin node_modules/extract-text-webpack-plugin/dist node_modules/css-loader/index.js!node_modules/less-loader/dist/cjs.js!src/less/index.less:
+ [0] ./node_modules/css-loader!./node_modules/less-loader/dist/cjs.js!./src/less/index.less 213 bytes {0} [built]
+ + 1 hidden module
+```
+
+That is because by default `webpack` resolves only files that ends with `.js`. We need to add the following element to our `webpack.config.js`
+```
+resolve: {
+ extensions: ['.js', '.jsx']
+}
+```
+
+and change our test for the js extension to: `test: /\.js$|\.jsx$/,` in our rules.
+
+Our `Hello` component can be as well written very differently but outputting the exact same thing:
+
+```javascript
+export const Hello2 = () => (<div>Hello React!</div>);
+```
+
+This is called a `stateless functional component` and is useful for dumb/presentational components. Presentational components focus on the UI rather than behavior, so it’s important to avoid using state in presentational components.
+
+#### props
+
+Any given react component accepts parameters which are called [`props`](https://reactjs.org/docs/components-and-props.html):
+
+```javascript
+import React, { Component } from 'react';
+
+export class Hello extends Component {
+ render() {
+ const { from } = this.props;
+ return (<div>Hello React!</div>);
+ }
+}
+
+export const Hello2 = ({from}) => (<div>Hello React from {from}!</div>);
+```
+
+In our common.js we do now `<Hello from="common.js"/>` and see something like `Hello React from common.js!` in the resulting html.
+
+As side note `({from})` in the `Hello2` component is the same as `(props)` and then doing `const {from} = props`
+
+#### PropTypes
+
+As your app grows, you can catch a lot of bugs with typechecking. For some applications, you can use JavaScript extensions like Flow or TypeScript to typecheck your whole application. But even if you don’t use those, React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special [propTypes](https://reactjs.org/docs/typechecking-with-proptypes.html#proptypes) property.
+
+React.PropTypes has moved into a different package since React v15.5. We need to use the prop-types library instead `npm i -D -E prop-types`
+
+```javascript
+Hello.propTypes = {
+ from: PropTypes.string,
+};
+
+Hello2.propTypes = Hello.propTypes;
+```
+
+Now if we e.g. pass a number from common.js `<Hello from={1} />` we will see in the console:
+
+```
+Warning: Failed prop type: Invalid prop `from` of type `number` supplied to `Hello2`, expected `string`.
+ in Hello2
+```
+
+#### defaultProps
+
+defaultProps can be defined as a property on the component class itself, to set the default props for the class. This is used for undefined props, but not for null props.
+
+```javascript
+Hello.defaultProps = {
+ from: 'Hello.jsx'
+};
+```
+
+Then using `<Hello/>` will return `Hello React from Hello.jsx!`
+
+In case you are using es6 you can do the same without having to use `defaultProps`, so `<Hello2 />` will return
+`Hello React from Hello2!` when lokking like:
+
+```javascript
+export const Hello2 = ({from = 'Hello2'}) => (<div>Hello React from {from}!</div>);
+```
+
+**HEADSUP** if you pass `null` as value for `from` you will see in both cases `Hello React from !`
+
+#### props.children
+
+props.children is available on every component. It contains the content between the opening and closing tags of a component. For example:
+
+```javascript
+ReactDOM.render(<Hello2>I am a child</Hello2>, root);
+```
+
+and
+
+```javascript
+export const Hello2 = ({from = 'Hello2', children}) => (<div>
+ Hello React from {from}!
+ { children && <p>{children}</p>}
+</div>);
+
+```
+
+will render
+
+```html
+<div>Hello React from Hello2!<p>I am a child</p></div>
+```
+
+The expression `{ children && <p>{children}</p>}` means if children are not null return the `<p/>` expression.
+
+Until now we could only use one component in our common.js this is not practical on the long run. Here `composing components` are coming in handy.
+
+#### composing components
+
+Components can refer to other components in their output. This lets us use the same component abstraction for any level of detail. A button, a form, a dialog, a screen: in React apps, all those are commonly expressed as components.
+
+Let us create an `App` component (we using a new feature of v.16: you can now return an array of elements from a component’s render method.):
+
+```javascript
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Hello, Hello2 } from './Hello';
+
+export class App extends Component {
+ // No need to wrap list items in an extra element!
+ render() {
+ // Don't forget the keys :)
+ return [ <Hello key="1"/>, <Hello2 key="2"/>]
+ }
+}
+
+App.propTypes = {
+ children: PropTypes.node,
+};
+```
+
+#### refactor index.html use a Layout component
+
+Let us refactor our index.hmtl to be a simple skeleton and not returning any content on its own:
+
+```html
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>JavaScript Example</title>
+ <!-- link the generated css -->
+ <link href="dist/common.css" rel="stylesheet" title="Default Style"/>
+ </head>
+ <body>
+ <div id="react"></div>
+ <!-- generated the css we want to use AND mount React -->
+ <script src="dist/vendor.js"></script>
+ <script src="dist/common.js"></script>
+ <script src="dist/index.js"></script>
+ </body>
+</html>
+```
+
+Layout.jsx:
+
+```javascript
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+
+export class Layout extends Component {
+ render() {
+ return (<div className="container">
+ <div className="content">
+ {this.props.children}
+ </div>
+ </div>);
+ }
+}
+
+Layout.propTypes = {
+ children: PropTypes.node,
+};
+```
+
+common.js
+
+```javascript
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { App } from './components/App';
+import { Layout } from './components/Layout';
+import '../less/index.less'; // tell webpack to request the transpiling of less to css
+
+const root = document.getElementById('react');
+ReactDOM.render(<Layout><App/></Layout>, root);
+```
+
+You may have noticed that we lost our console component.
+We had patched the global console.log which is not such a good idea.
+Let us create a Log component for React.
+
+#### [State and Lifecycle](https://reactjs.org/docs/state-and-lifecycle.html)
+
+There are two types of data that control a component: props and state. props are set by the parent and they are fixed throughout the lifetime of a component. For data that is going to change, we have to use state.
+
+In general, you should initialize state in the constructor, and then call setState when you want to change it.
+
+Let us implement in our `Layout` component the manipulation of state:
+
+```javascript
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+
+/**
+ * Beware: React setState is asynchronous!
+ * Calling setState multiple times during a single update cycle can lead to nasty bugs, because
+ * setState is asynchronous, subsequent calls in the same update cycle will overwrite previous
+ * updates, and the previous changes will be lost.
+ *
+ * This wrapper uses the alternative setState calling convention
+ * @see https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
+ * @param message the message you want to add to the log console
+ */
+const addMessage = (message) => (previousState) => {
+ // the the logs from the earlier state
+ const returnState = [...previousState.logs];
+ // add our message
+ returnState.push(message);
+ // now return our current state
+ return {logs: returnState};
+};
+
+
+export class Layout extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {logs: []};
+ }
+ componentDidMount() {
+ const addLog = message => this.setState(addMessage(message));
+ addLog('3 rendering Layout finished');
+ }
+ render() {
+ const { logs } = this.state;
+ const addLog = message => this.setState(addMessage(message));
+ return (<div className="container">
+ <div className="content">
+ {React.cloneElement(this.props.children, { addLog })}
+ </div>
+ {logs && logs.length > 0 && <div id="log">
+ <div>Logs:</div>
+ { logs.map((item,index) => <p key={index}>{item}</p>) }
+ </div>}
+ </div>);
+ }
+}
+
+Layout.propTypes = {
+ children: PropTypes.node,
+};
+
+```
+
+Note how we pass props to the base `constructor`:
+```javascript
+ constructor(props) {
+ super(props);
+ this.state = {logs: []};
+ }
+```
+
+Class components should always call the base constructor with props.
+
+We created a wrapper function around setState to make sure that we do not
+lose any state changes.
+
+```javascript
+const addLog = message => this.setState(addMessage(message));
+```
+
+This function we are "passing down" to our children by using `React.cloneElement` which allows to augment the properties.
+
+```javascript
+{React.cloneElement(this.props.children, { addLog })}
+```
+
+We then add the `componentDidMount` lifecycle to our `Layout` and as well to the `Hello` component.
+That is because if you would try to change the state in a `render` you will get following error in the console:
+
+```
+Warning: Cannot update during an existing state transition (such as within `render` or another component's constructor).
+Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern,
+but can be moved to `componentWillMount`
+```
+
+The former looks like:
+```javascript
+ componentDidMount() {
+ const addLog = message => this.setState(addMessage(message));
+ addLog('3 rendering Layout finished');
+ }
+```
+
+and the later looks like:
+
+```javascript
+ componentDidMount() {
+ const { addLog } = this.props;
+ addLog('1 rendering Hello finished');
+ addLog('2 rendering Hello finished');
+ }
+```
+
+The main difference is that we use `this.props.addLog` in our child component.
+
+The result will look like:
+
+```html
+<div id="log">
+ <div>Logs:</div>
+ <p>1 rendering Hello finished</p>
+ <p>2 rendering Hello finished</p>
+ <p>3 rendering Layout finished</p>
+</div>
+```
+
+You can see that first our `Hello` component finished the mount and in the end our `Layout`
+
+#### Show logs onClick
+
+We may not want to see all the time the log component so let us create a button which will show the log console onClick.
+
+```javascript
+{
+ constructor(props) {
+ super(props);
+ this.state = {
+ logs: [],
+ showLog: false, // initially do not show console
+ };
+ }
+
+ render() {
+ const { logs, showLog } = this.state;
+ const addLog = message => this.setState(addMessage(message));
+ return (<div className="container">
+ ...
+ { !showLog && <button onClick={()=>this.setState({ showLog: true })}>Show Log</button>}
+ { showLog && <div id="log">
+ <div>Logs: <button onClick={()=>this.setState({ showLog: false })}>Hide Log</button></div>
+ { logs.map((item,index) => <p key={index}>{item}</p>) }
+ </div>}
+ ...
+ </div>);
+ }
+}
+
+```
+
+This examples shows that state updates are merged, since changing `this.setState({ showLog: true })`
+is not changing `this.state.logs`.
+
+#### Creating routes
+
+As soon as you have different pages that you want to expose with your app you need to define routes
+to tell react when to render the different views.
+We will use [React Router](https://github.com/ReactTraining/react-router) in the v4 which is not
+really compatible with earlier versions of that library.
+
+First let us activate `stage-2` support for babel, so we can use [Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator)
+in our sources like `const objClone = { ...obj };`
+
+Further we will install a new webpack plugin so we can load svg files and serve them.
+
+```bash
+npm i -S -E react-router-dom
+npm i -D -E babel-preset-stage-2 svg-url-loader
+```
+
+We need to tell babel and webpack that we support `stage-2` now. For this we will add it to `.babelrc` in the `presets`
+
+```json
+{
+ "presets": ["env", "react", "stage-2"]
+}
+```
+
+Since we are starting to develop a real webapp we want to be able to serve svg images. For this we
+need to add a new loader to our `webpack.config.js`. While we have opened the `webpack.config.js`
+we need to activate [history-api-fallback](https://github.com/webpack/webpack-dev-server/tree/master/examples/history-api-fallback)
+in the `devServer` section to make sure all our routes are using our `index.hmtl`. Further we need to tell webpack to resolve
+not only `js` extensions but as well `jsx` and modify our loader regex to include it as well in the `babel-loader`.
+
+```
+...
+module.exports = {
+ devServer: {
+ ...
+ historyApiFallback: true
+ },
+...
+ module: {
+ rules: [
+ {
+ test: /\.js$|\.jsx$/,
+ exclude: /node_modules/,
+ use: 'babel-loader'
+ },
+...
+ {
+ test: /\.svg$/,
+ use: 'svg-url-loader'
+ }
+ ],
+ },
+...
+ resolve: {
+ extensions: ['.js', '.jsx']
+ }
+};
+```
+
+We are using this loader in our index.less
+
+```less
+.Logo {
+ background-image: url('svg/React-icon.svg');
+ background-repeat: no-repeat;
+ width: 75px;
+ height: 60px;
+ margin-top: 5px;
+}
+.NotFound{
+ background-image: url('svg/not-found.svg');
+ background-repeat: no-repeat;
+ background-size: 400px 175px;
+ height: 200px;
+ width: 100vh;
+ margin: auto;
+ margin-top: 25px;
+
+}
+```
+
+Until now we had just a couple of files to provide some example of what you can do and how. Let us now create
+a lot more to simulate a real world example. The following files are added
+
+```bash
+src/js/components
+├── App.jsx # main component exposing Header and Main
+├── Header.jsx # component to create logo and navigation tabs
+├── Home.jsx # our main index page showing the usage of moment lib
+├── Main.jsx # component to hold all routes and map them to components
+├── NotFound.jsx # in case the route is not defined show a 404
+├── Team.jsx # sample component to show nested routes and data drill down
+└── teams.js # sample teams used
+```
+
+##### Header.js
+
+```javascript
+import React from 'react';
+import { Link, Route } from 'react-router-dom';
+
+export const tabs = [{
+ to:'/',
+ caption:'Home',
+ exact: true,
+}, {
+ to: '/team',
+ caption: 'Teams',
+}];
+
+export const TabsRender = ({ match: { url }}) => <nav><ul>
+ {tabs.map(tab => {
+ let active = false;
+ const { exact, to, caption} = tab;
+ if (exact) {
+ active = url === to;
+ } else {
+ active = url.indexOf(to) > -1;
+ }
+ return <li key={to} className={ active ? 'active' : ''}>
+ <Link to={to}>{caption}</Link>
+ </li>})
+ }
+</ul></nav>;
+
+TabsRender.propTypes = {
+ match: PropTypes.shape({ url: PropTypes.string })
+};
+
+export const Header = () => (<div className="links">
+ <section className="header">
+ <div className="Logo"></div>
+ <Route path='*' componet={TabsRender} />
+ </section>
+</div>);
+```
+
+We are creating here a typical header where you have a logo on the left side and on the right hand a tap-navigation.
+
+We are matching all path `<Route path='*' componet={TabsRender} />` and then leverage to the `TabsRender` component.
+Here use the `({ match: { url }})` to
+evaluate which tab is currently active, we can use `exact` to match `url === to` or see whether our current path is based
+on the team section ` url.indexOf(to) > -1`.
+
+You may have noticed by now, that I am a big fan of newer javascript syntax. Let us see the [destruction assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
+we use in the `TabRender`.
+
+We know from the [match](https://reacttraining.com/react-router/core/api/match) documentation that we can expect
+`params, isExact, path, url` being passed to a `Route` child. We further know from the [Route](https://reacttraining.com/react-router/core/api/Route/Route-props)
+documentation that besides `match` we can expect `location` and `history` as properties passed to our `TabsRender`
+
+```javascript
+const props = {
+ location,
+ history,
+ match: {
+ url,
+ path,
+ params,
+ isExact
+ }}; // this are the props that are passed from the Route
+// since we are solely interested in the url to make our comparison we can "extract" the value and define a fallback
+// beware of match being null then the fallback will not work, you should use undefined instead of null
+const { match: { url } = { url: 'none'}} = props;
+// in old shool js you need to do the following to prevent NPE
+const oldSchoolUrl = props.match ? props.match.url : 'none';
+// the following should be true in case match had **not** being null
+url === oldSchoolUrl
+```
+
+Now have let us have a look on the `Main` compontent.
+
+```javascript
+import { Switch, Route } from 'react-router-dom';
+import { Home } from './Home';
+import { Teams } from './Team';
+import { NotFound} from './NotFound';
+
+export const Main = () => (
+ <main>
+ <Switch>
+ <Route exact path='/' component={Home}/>
+ <Route path='/team' component={Teams}/>
+ <Route path='*' component={NotFound}/>
+ </Switch>
+ </main>
+);
+```
+
+Here we are using the `Route` component slightly different then before since we now are matching different `path`.
+The [Switch](https://reacttraining.com/react-router/core/api/Switch)
+elemet will render the first child `<Route>` or `<Redirect>` that matches the location. Our first route uses `exact` because if
+we would not use it, it will be matched on every request and hence our other matches would never be executed.
+
+The `<Route path='*' component={NotFound}/>` as last route will make sure we will always return at least the 404 page in case we do not
+match any other path.
+
+The `Home` component is real basic put I want to point to the default import of `App2` `import App2 from './App2';` because it shows the usages of HOC.
+
+##### HOC [higher order components](https://reactjs.org/docs/higher-order-components.html)
+
+In our first version before using the router we used the `Layout` component to pass the `addMessage` method to the children. However
+we do not need our Layout component anymore but we need that method. The solution is to create a HOC.
+
+> A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature.
+> <cite>[higher order components](https://reactjs.org/docs/higher-order-components.html)</cite>
+
+Our HOC component looks like:
+
+```javascript
+import React, {Component} from 'react';
+
+const addMessageAction = (message) => (previousState) => {
+ // the the logs from the earlier state
+ const returnState = [...previousState.logs];
+ // add our message
+ returnState.push(message);
+ // now return our current state
+ return {logs: returnState};
+};
+
+export const addMessage = ComposedComponent => class extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ logs: [],
+ showLog: false,
+ };
+ }
+ componentDidMount() {
+ const addLog = message => this.setState(addMessageAction(message));
+ addLog('HOC component did mount');
+ }
+ render() {
+ const { logs, showLog } = this.state;
+ const addLog = message => this.setState(addMessageAction(message));
+ const messageOutput = showLog ? (<div id="log">
+ <div>Logs: <button onClick={()=>this.setState({ showLog: false })}>Hide Log</button></div>
+ { logs.map((item,index) => <p key={index}>{item}</p>) }
+ </div>) : (<button onClick={()=>this.setState({ showLog: true })}>Show Log</button>);
+ return (<ComposedComponent
+ addLog={addLog}
+ messageOutput={messageOutput}
+ {...this.props}
+ />);
+ }
+};
+```
+
+What we are doing here is basically augment the component that is passed to us with two new properties `addMessage` as a function
+and `messageOutput` as DOM element.
+
+We are using it in our `App2` as follows and define the `ComposedComponent` as `export default`:
+
+```javascript
+...
+import { addMessage} from './addMessage';
+...
+export default addMessage(App2);
+// leads to:
+export const addMessage = App2 => class extends Component {
+ ...
+ return (<App2
+ addLog={addLog}
+ messageOutput={messageOutput}
+ {...this.props}
+ />);
+}
+```
+
+##### nested routes
+
+In `Team.jsx` we are using nested routes to create an overview page and the team detail page.
+In the Main component we have declared to match the Teams, so we only
+enter when the `url` starts with `/team`. The following can lead to an error
+which will stop our app from rendering.
+
+```javascript
+...
+export const Teams = () => <div className="TeamContainer">
+ <Switch>
+ <Route exact path='/team' component={ TeamsRender }/>
+ <Route path='/team/:name' component={ Members } />
+ </Switch>
+</div>;
+```
+
+In the case that the team name does not exist we will run into a NPE becasue of
+the fact that `getTeams(name)[0]` return null.
+
+```javascript
+export const Members = ({ match: { params: { name }}}) => {
+ const { displayName, image, members } = getTeams(name)[0];
+ return (<div className="team">
+ <div className="spacer">
+ <img className="animate" src={image} alt={displayName} title={displayName}/>
+ </div>
+ { members.map(item => <Member {...{...item, key: item.character}} />)}
+ </div>);
+};
+```
+
+##### [Error Boundaries](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html)
+
+First of all let us use a new feature of react 16 where we can
+use [error boundaries](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html)
+to prevent our app from breaking.
+
+> Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
+> <cite>[error boundaries](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html)</cite>
+
+We want to be able to catch the error that can happen in different places
+for our application. This calls to create another HOC, which shows us that we can
+return an error answer instead of the component that has errors.
+
+```javascript
+import React, {Component} from 'react';
+
+export const Alert = ({error: { message}, info: {componentStack}}) => {
+ const stack = componentStack.split(/\n/)
+ .filter(content => content !== '')
+ .map(item => <li>{item}</li>);
+ return (<div className="Alert">
+ <div className="Alert-Flex">
+ <div className="border"> </div>
+ <div className="title">
+ { message }
+ </div>
+ </div>
+ <div className="Alert-Flex">
+ <div className="border"> </div>
+ <div className="message">
+ <ul>
+ { stack }
+ </ul>
+ </div>
+ </div>
+ </div>);
+};
+export const addErrorBounds = ComposedComponent => class extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ hasError: false,
+ message: undefined,
+ };
+ }
+ componentDidCatch(error, info) {
+ // Display fallback UI
+ this.setState({hasError: true, message: {error, info}});
+ }
+ render() {
+ // if we have errors we return an alert and not the ComposedComponent
+ if (this.state.hasError) {
+ const { error, info } = this.state.message;
+ return <Alert {...{error, info}}/>;
+ }
+ return (<ComposedComponent {...this.props} />);
+ }
+};
+```
+
+Now we can add it to our parent `App` component as a `catch all error anywhere`
+approach with `export default addErrorBounds(App);` and use the default import for our common.js.
+
+However that will basically make our app unusable since we do not render any child.
+
+When we use it on a component level like follows, we can prevent only the problematic component to be rendered
+but the rest of our app will work as expected.
+```javascript
+// showing how you can use HOC to reuse error boundaries
+export const BoundedMembers = addErrorBounds(Members);
+export const Teams = () => <div className="TeamContainer">
+ <Switch>
+ <Route exact path='/team' component={ TeamsRender }/>
+ <Route path='/team/:name' component={ BoundedMembers } />
+ </Switch>
+</div>;
+```
+
+In case that we want to look up a team that triggers a NPE we will now display
+an alert instead to break our app, which still provides means to go to the registered
+views. In real life you would now go ahead and prevent that non-existing teams can be returned, however
+for demonstration purposes of the error boundaries we will not do that.
+
diff --git a/src/js/common.js b/src/js/common.js
index 10ad68e..5b98c75 100644
--- a/src/js/common.js
+++ b/src/js/common.js
@@ -1,6 +1,8 @@
-import '../less/index.less'; // tell webpack to request the transpiling of less to css
import React from 'react';
import ReactDOM from 'react-dom';
+import { BrowserRouter as Router } from 'react-router-dom';
+import App from './components/App';
+import '../less/index.less'; // tell webpack to request the transpiling of less to css
const root = document.getElementById('react');
-ReactDOM.render(<div>Hello React!</div>, root);
+ReactDOM.render(<Router><App/></Router>, root);
diff --git a/src/js/components/Alert.jsx b/src/js/components/Alert.jsx
new file mode 100644
index 0000000..518a312
--- /dev/null
+++ b/src/js/components/Alert.jsx
@@ -0,0 +1,44 @@
+import React, {Component} from 'react';
+
+export const Alert = ({error: { message}, info: {componentStack}}) => {
+ const stack = componentStack.split(/\n/)
+ .filter(content => content !== '')
+ .map(item => <li>{item}</li>);
+ return (<div className="Alert">
+ <div className="Alert-Flex">
+ <div className="border"> </div>
+ <div className="title">
+ { message }
+ </div>
+ </div>
+ <div className="Alert-Flex">
+ <div className="border"> </div>
+ <div className="message">
+ <ul>
+ { stack }
+ </ul>
+ </div>
+ </div>
+ </div>);
+};
+export const addErrorBounds = ComposedComponent => class extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ hasError: false,
+ message: undefined,
+ };
+ }
+ componentDidCatch(error, info) {
+ // Display fallback UI
+ this.setState({hasError: true, message: {error, info}});
+ }
+ render() {
+ // if we have errors we return an alert and not the ComposedComponent
+ if (this.state.hasError) {
+ const { error, info } = this.state.message;
+ return <Alert {...{error, info}}/>;
+ }
+ return (<ComposedComponent {...this.props} />);
+ }
+};
\ No newline at end of file
diff --git a/src/js/components/App.jsx b/src/js/components/App.jsx
new file mode 100644
index 0000000..b475fbd
--- /dev/null
+++ b/src/js/components/App.jsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import { Header } from './Header';
+import { Main } from './Main';
+import { addErrorBounds } from './Alert';
+
+export const App = () => (<div className="container">
+ <Header/>
+ <Main/>
+</div>);
+
+export default addErrorBounds(App);
\ No newline at end of file
diff --git a/src/js/components/App2.jsx b/src/js/components/App2.jsx
new file mode 100644
index 0000000..282c53b
--- /dev/null
+++ b/src/js/components/App2.jsx
@@ -0,0 +1,22 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Hello, Hello2 } from './Hello';
+import { addMessage} from './addMessage';
+
+export class App2 extends Component {
+ componentDidMount() {
+ const { addLog =(message) => console.log(message) } = this.props;
+ addLog('rendering APP2 finished');
+ }
+ render() {
+ const { messageOutput = (<div>Nothing</div>) } = this.props;
+ return <div>Second page <Hello {...this.props} /><Hello2 {...this.props} /> { messageOutput}</div>;
+ }
+}