├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.yml ├── _includes ├── footer.html ├── header.html ├── hublink.html └── skipper.html ├── _layouts ├── default.html └── tutorial.html ├── css └── main.css ├── img ├── formidable-logo.svg ├── part-1.jpg ├── part-2.jpg └── part-3.jpg ├── index.html ├── js └── main.js ├── part-1-fundamentals.md ├── part-2-building-an-app.md └── part-3-styles-with-radium.md /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | gem 'github-pages' 2 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | specs: 3 | RedCloth (4.2.9) 4 | activesupport (~> 4.2.2) 5 | i18n (~> 0.7) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | blankslate (2.1.2.4) 11 | celluloid (0.16.0) 12 | timers (~> 4.0.0) 13 | classifier-reborn (2.0.3) 14 | fast-stemmer (~> 1.0) 15 | coffee-script (2.3.0) 16 | coffee-script-source 17 | execjs 18 | coffee-script-source (1.9.1) 19 | colorator (0.1) 20 | execjs (2.3.0) 21 | fast-stemmer (1.0.2) 22 | ffi (1.9.6) 23 | gemoji (2.1.0) 24 | github-pages (33) 25 | RedCloth (= 4.2.9) 26 | github-pages-health-check (~> 0.2) 27 | jekyll (= 2.4.0) 28 | jekyll-coffeescript (= 1.0.1) 29 | jekyll-mentions (= 0.2.1) 30 | jekyll-redirect-from (= 0.6.2) 31 | jekyll-sass-converter (= 1.2.0) 32 | jekyll-sitemap (= 0.6.3) 33 | jemoji (= 0.4.0) 34 | kramdown (= 1.5.0) 35 | liquid (= 2.6.1) 36 | maruku (= 0.7.0) 37 | mercenary (~> 0.3) 38 | pygments.rb (= 0.6.1) 39 | rdiscount (= 2.1.7) 40 | redcarpet (= 3.1.2) 41 | terminal-table (~> 1.4) 42 | github-pages-health-check (0.2.1) 43 | net-dns (~> 0.6) 44 | public_suffix (~> 1.4) 45 | hitimes (1.2.2) 46 | html-pipeline (1.9.0) 47 | activesupport (~> 4.2.2) 48 | nokogiri (~> 1.4) 49 | i18n (0.7.0) 50 | jekyll (2.4.0) 51 | classifier-reborn (~> 2.0) 52 | colorator (~> 0.1) 53 | jekyll-coffeescript (~> 1.0) 54 | jekyll-gist (~> 1.0) 55 | jekyll-paginate (~> 1.0) 56 | jekyll-sass-converter (~> 1.0) 57 | jekyll-watch (~> 1.1) 58 | kramdown (~> 1.3) 59 | liquid (~> 2.6.1) 60 | mercenary (~> 0.3.3) 61 | pygments.rb (~> 0.6.0) 62 | redcarpet (~> 3.1) 63 | safe_yaml (~> 1.0) 64 | toml (~> 0.1.0) 65 | jekyll-coffeescript (1.0.1) 66 | coffee-script (~> 2.2) 67 | jekyll-gist (1.1.0) 68 | jekyll-mentions (0.2.1) 69 | html-pipeline (~> 1.9.0) 70 | jekyll (~> 2.0) 71 | jekyll-paginate (1.1.0) 72 | jekyll-redirect-from (0.6.2) 73 | jekyll (~> 2.0) 74 | jekyll-sass-converter (1.2.0) 75 | sass (~> 3.2) 76 | jekyll-sitemap (0.6.3) 77 | jekyll-watch (1.2.1) 78 | listen (~> 2.7) 79 | jemoji (0.4.0) 80 | gemoji (~> 2.0) 81 | html-pipeline (~> 1.9) 82 | jekyll (~> 2.0) 83 | json (1.8.2) 84 | kramdown (1.5.0) 85 | liquid (2.6.1) 86 | listen (2.8.5) 87 | celluloid (>= 0.15.2) 88 | rb-fsevent (>= 0.9.3) 89 | rb-inotify (>= 0.9) 90 | maruku (0.7.0) 91 | mercenary (0.3.5) 92 | mini_portile (0.6.2) 93 | minitest (5.5.1) 94 | net-dns (0.8.0) 95 | nokogiri (1.6.6.2) 96 | mini_portile (~> 0.6.0) 97 | parslet (1.5.0) 98 | blankslate (~> 2.0) 99 | posix-spawn (0.3.10) 100 | public_suffix (1.4.6) 101 | pygments.rb (0.6.1) 102 | posix-spawn (~> 0.3.6) 103 | yajl-ruby (~> 1.3.1) 104 | rb-fsevent (0.9.4) 105 | rb-inotify (0.9.5) 106 | ffi (>= 0.5.0) 107 | rdiscount (2.1.7) 108 | redcarpet (3.1.2) 109 | safe_yaml (1.0.4) 110 | sass (3.4.12) 111 | terminal-table (1.4.5) 112 | thread_safe (0.3.4) 113 | timers (4.0.1) 114 | hitimes 115 | toml (0.1.2) 116 | parslet (~> 1.5.0) 117 | tzinfo (1.2.2) 118 | thread_safe (~> 0.1) 119 | yajl-ruby (~> 1.3.1) 120 | 121 | PLATFORMS 122 | ruby 123 | 124 | DEPENDENCIES 125 | github-pages 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This is a Jekyll site. To run it: 4 | 5 | ``` 6 | bundle install 7 | jekyll serve 8 | ``` 9 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | exclude: [ 2 | "Gemfile", 3 | "Gemfile.lock" 4 | ] 5 | markdown: "redcarpet" 6 | redcarpet: 7 | extensions: [with_toc_data] 8 | -------------------------------------------------------------------------------- /_includes/footer.html: -------------------------------------------------------------------------------- 1 | {% if include.tutorial %} 2 | {% assign extra_class = 'Primary' %} 3 | {% endif %} 4 | 5 | 16 | -------------------------------------------------------------------------------- /_includes/header.html: -------------------------------------------------------------------------------- 1 | {% if include.details %} 2 | {% assign list_class = 'TocList' %} 3 | {% else %} 4 | {% assign list_class = 'NavList' %} 5 | {% endif %} 6 | 7 |
8 | 14 | 15 | 69 | 70 | 71 | Need React.js Consulting? Contact Us. 72 | 73 |
74 | -------------------------------------------------------------------------------- /_includes/hublink.html: -------------------------------------------------------------------------------- 1 | {{ include.text }} 2 | -------------------------------------------------------------------------------- /_includes/skipper.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ page.title }} 9 | 10 | {% if page.description %} 11 | 12 | {% endif %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | {{ content }} 22 |
23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /_layouts/tutorial.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
6 | {% include header.html %} 7 |
8 | 9 | 73 | 74 |
75 |
76 |

{{ page.title }}

77 | 78 | {% if page.description %} 79 |

80 | {{ page.description }} 81 |

82 | {% endif %} 83 | 84 | {{ content }} 85 | 86 |
87 |
88 | 89 | {% include footer.html tutorial="true" %} 90 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* Base */ 2 | 3 | html { 4 | font-family: proxima-nova, Helvetica Neue, Helvetica, Arial, sans-serif; 5 | line-height: 1.5; 6 | -moz-osx-font-smoothing: grayscale; 7 | -webkit-font-smoothing: antialiased; 8 | -ms-text-size-adjust: 100%; 9 | -webkit-text-size-adjust: 100%; 10 | } 11 | 12 | *, *:before, *:after { 13 | box-sizing: inherit; 14 | } 15 | 16 | body { 17 | box-sizing: border-box; 18 | margin: 0; 19 | } 20 | 21 | article, 22 | aside, 23 | details, 24 | figcaption, 25 | figure, 26 | footer, 27 | header, 28 | hgroup, 29 | main, 30 | menu, 31 | nav, 32 | section, 33 | summary { 34 | display: block; 35 | } 36 | 37 | a { 38 | background-color: transparent; 39 | } 40 | 41 | a:active, 42 | a:hover { 43 | outline: 0; 44 | } 45 | 46 | b, 47 | strong { 48 | font-weight: bold; 49 | } 50 | 51 | dfn { 52 | font-style: italic; 53 | } 54 | 55 | h1 { 56 | font-size: 2em; 57 | margin: 0.67em 0; 58 | } 59 | 60 | img { 61 | border: 0; 62 | max-width: 100%; 63 | } 64 | 65 | pre { 66 | overflow: auto; 67 | } 68 | 69 | button, 70 | input, 71 | optgroup, 72 | select, 73 | textarea { 74 | color: inherit; 75 | font: inherit; 76 | margin: 0; 77 | } 78 | 79 | button { 80 | overflow: visible; 81 | } 82 | 83 | button, 84 | select { 85 | text-transform: none; 86 | } 87 | 88 | button, 89 | html input[type="button"], 90 | input[type="reset"], 91 | input[type="submit"] { 92 | -webkit-appearance: button; 93 | cursor: pointer; 94 | } 95 | 96 | button[disabled], 97 | html input[disabled] { 98 | cursor: default; 99 | } 100 | 101 | button::-moz-focus-inner, 102 | input::-moz-focus-inner { 103 | border: 0; 104 | padding: 0; 105 | } 106 | 107 | input { 108 | line-height: normal; 109 | } 110 | 111 | /* Default Margins */ 112 | 113 | h1, 114 | h2, 115 | h3, 116 | h4, 117 | h5, 118 | h6, 119 | hgroup, 120 | ul, 121 | ol, 122 | dd, 123 | p, 124 | figure, 125 | pre, 126 | table, 127 | fieldset, 128 | hr { 129 | margin-top: 0; 130 | margin-bottom: 1.5rem; 131 | } 132 | 133 | h1:last-child, 134 | h2:last-child, 135 | h3:last-child, 136 | h4:last-child, 137 | h5:last-child, 138 | h6:last-child, 139 | hgroup:last-child, 140 | ul:last-child, 141 | ol:last-child, 142 | dd:last-child, 143 | p:last-child, 144 | figure:last-child, 145 | pre:last-child, 146 | table:last-child, 147 | fieldset:last-child, 148 | hr:last-child { 149 | margin-bottom: 0; 150 | } 151 | 152 | /* Layout */ 153 | 154 | html { 155 | background-color: #EEF9FD; 156 | color: #35424A; 157 | } 158 | 159 | a { 160 | color: #3571AB; 161 | } 162 | 163 | a:hover, 164 | a:focus { 165 | color: #35424A; 166 | } 167 | 168 | .Home { 169 | max-width: 1024px; 170 | margin: 0 auto; 171 | padding: 1.5rem 1rem; 172 | } 173 | 174 | .Primary { 175 | position: relative; 176 | 177 | padding: 1.5rem 1rem; 178 | } 179 | 180 | .Primary h2 { 181 | position: relative; 182 | 183 | margin-top: 2.5rem; 184 | margin-bottom: 0.5rem; 185 | } 186 | 187 | .HeadingLink { 188 | color: #35424A; 189 | text-decoration: none; 190 | } 191 | 192 | .Primary-content { 193 | max-width: 42em; 194 | margin: 0 auto; 195 | } 196 | 197 | .Logo { 198 | display: block; 199 | margin-bottom: 1rem; 200 | 201 | line-height: 1; 202 | text-align: center; 203 | text-decoration: none; 204 | text-transform: uppercase; 205 | text-rendering: optimizeLegibility; 206 | } 207 | 208 | .Logo-small { 209 | display: block; 210 | 211 | color: #7097b3; 212 | font-family: proxima-nova-extra-condensed, Helvetica Neue, Helvetica, Arial, sans-serif; 213 | font-size: 28px; 214 | font-weight: 400; 215 | } 216 | 217 | .Logo-large { 218 | position: relative; 219 | 220 | display: block; 221 | 222 | color: #35424A; 223 | font-size: 34px; 224 | font-weight: 700; 225 | letter-spacing: 0.05em; 226 | } 227 | 228 | .Logo-largeWrap { 229 | position: relative; 230 | 231 | display: inline-block; 232 | padding: 0 5px; 233 | 234 | background-color: #EEF9FD; 235 | } 236 | 237 | .Logo-large:before { 238 | content: ""; 239 | position: absolute; 240 | top: 0; 241 | right: 0; 242 | bottom: 2px; 243 | left: 0; 244 | 245 | margin: auto; 246 | height: 6px; 247 | border: solid #c6dbea; 248 | border-width: 1px 0; 249 | } 250 | 251 | .Nav { 252 | text-align: center; 253 | } 254 | 255 | .NavList { 256 | font-size: 0; 257 | } 258 | 259 | .NavList-cell { 260 | margin-bottom: 1rem; 261 | 262 | font-size: 1rem; 263 | vertical-align: top; 264 | } 265 | 266 | .NavList-cell:last-child { 267 | margin-bottom: 0; 268 | } 269 | 270 | .NavLink { 271 | display: inline-block; 272 | padding: 0 10px 2px; 273 | border-bottom: 2px solid transparent; 274 | 275 | color: #35424A; 276 | font-weight: 600; 277 | line-height: 1.2; 278 | text-decoration: none; 279 | } 280 | 281 | .NavLink:hover { 282 | border-color: #c6dbea; 283 | } 284 | 285 | .NavLink:hover .NavLink-head { 286 | color: #35424A; 287 | } 288 | 289 | .NavLink-head { 290 | display: block; 291 | 292 | color: #397BA9; 293 | font-size: 13px; 294 | font-weight: 700; 295 | letter-spacing: 0.1em; 296 | text-rendering: optimizeLegibility; 297 | text-transform: uppercase; 298 | } 299 | 300 | .NavLink-img { 301 | padding: 1rem 1.5rem 0; 302 | } 303 | 304 | .TocList { 305 | text-align: left; 306 | padding-bottom: 1rem !important; 307 | } 308 | 309 | .TocList-cell { 310 | overflow: hidden; 311 | margin-top: 2rem; 312 | } 313 | 314 | .TocList-cell .NavLink { 315 | border: none; 316 | } 317 | 318 | .TocList-cell:nth-child(even) .NavLink { 319 | float: right; 320 | } 321 | 322 | .TocList .NavLink-img { 323 | padding: 0; 324 | margin-bottom: 1rem; 325 | } 326 | 327 | .TocList .NavLink-subhead { 328 | margin-bottom: 8px; 329 | display: block; 330 | font-size: 1.6875rem; 331 | font-weight: 700; 332 | } 333 | 334 | .TocList-description { 335 | font-size: 18px; 336 | font-weight: 400; 337 | } 338 | 339 | .TocList .NavLink-content { 340 | overflow: hidden; 341 | } 342 | 343 | .Kicker { 344 | padding: 0.4em 0.6em; 345 | 346 | background-color: #397BA9; 347 | background-image: linear-gradient( 348 | to bottom right, 349 | rgba(0,0,0,0.15), 350 | rgba(255,255,255,0.15) 351 | ); 352 | color: #fff; 353 | font-size: 21px; 354 | line-height: 1.4; 355 | } 356 | 357 | .Sidebar { 358 | background-color: #397BA9; 359 | background-image: linear-gradient( 360 | to bottom right, 361 | rgba(0,0,0,0.15), 362 | rgba(255,255,255,0.15) 363 | ); 364 | box-shadow: inset 0 -1px rgba(0,0,0,0.2); 365 | } 366 | 367 | .FlexEmbed { 368 | display: block; 369 | overflow: hidden; 370 | position: relative; 371 | } 372 | 373 | .FlexEmbed-ratio { 374 | display: block; 375 | padding-bottom: 100%; 376 | width: 100%; 377 | } 378 | 379 | .FlexEmbed-ratio--16by9 { 380 | padding-bottom: 56.25%; 381 | } 382 | 383 | .FlexEmbed-ratio--4by3 { 384 | padding-bottom: 75%; 385 | } 386 | 387 | .FlexEmbed-content { 388 | bottom: 0; 389 | height: 100%; 390 | left: 0; 391 | position: absolute; 392 | top: 0; 393 | width: 100%; 394 | } 395 | 396 | .Sidebar a { 397 | color: #FFC770; 398 | font-weight: 600; 399 | } 400 | 401 | .Sidebar a:hover, 402 | .Sidebar a:focus { 403 | color: #FFEFD6; 404 | } 405 | 406 | .SidebarRow { 407 | padding: 1.5rem 1rem; 408 | border-bottom: 1px solid rgba(0,0,0,0.1); 409 | } 410 | 411 | .SidebarRow:last-child { 412 | border-bottom: 0; 413 | } 414 | 415 | @media (min-width: 600px) { 416 | .Home { 417 | padding: 1.5rem 3rem; 418 | } 419 | 420 | .Primary { 421 | padding: 1.5rem 3rem; 422 | 423 | font-size: 1.125rem; 424 | } 425 | 426 | .SidebarRow { 427 | padding: 1.5rem; 428 | } 429 | 430 | .NavList-cell { 431 | display: inline-block; 432 | width: 33.3333%; 433 | margin-bottom: 0; 434 | } 435 | 436 | .Logo { 437 | margin-bottom: 2rem; 438 | } 439 | 440 | .HeadingLink:before { 441 | content: '\00269B'; 442 | position: absolute; 443 | top: 1px; 444 | right: 100%; 445 | margin-right: 10px; 446 | 447 | color: #7097b3; 448 | font-size: 24px; 449 | -moz-osx-font-smoothing: auto; 450 | -webkit-font-smoothing: subpixel-antialiased; 451 | } 452 | 453 | .HeadingLink:hover:before { 454 | color: #35424A; 455 | } 456 | 457 | .TocList-cell { 458 | margin-top: 3rem; 459 | } 460 | 461 | .TocList .NavLink-img { 462 | float: left; 463 | width: 33.3333%; 464 | margin-right: 24px; 465 | margin-bottom: 0; 466 | } 467 | } 468 | 469 | @media (min-width: 900px) { 470 | .Primary { 471 | min-width: 60%; 472 | width: calc(100% - 600px); 473 | } 474 | 475 | .Sidebar { 476 | position: fixed; 477 | top: 0; 478 | right: 0; 479 | bottom: 0; 480 | width: 40%; 481 | max-width: 600px; 482 | 483 | overflow: auto; 484 | -webkit-overflow-scrolling: touch; 485 | 486 | box-shadow: inset 1px 0 rgba(0,0,0,0.2); 487 | } 488 | } 489 | 490 | @media (min-width: 1250px) { 491 | .Primary { 492 | min-width: 50%; 493 | } 494 | 495 | .Sidebar { 496 | width: 50%; 497 | } 498 | } 499 | 500 | .SidebarDark { 501 | background-color: rgba(0,0,0,0.4); 502 | } 503 | 504 | .SidebarMed { 505 | background-color: rgba(0,0,0,0.25); 506 | } 507 | 508 | .SidebarHeading { 509 | margin: 0; 510 | 511 | color: #fff; 512 | text-transform: uppercase; 513 | font-weight: 700; 514 | letter-spacing: 0.1em; 515 | font-size: 13px; 516 | text-rendering: optimizeLegibility; 517 | } 518 | 519 | .HorizontalList { 520 | margin: 0; 521 | font-size: 0; 522 | line-height: 1.2; 523 | } 524 | 525 | .HorizontalList dt { 526 | display: inline-block; 527 | width: 75px; 528 | padding-top: 3px; 529 | 530 | vertical-align: top; 531 | } 532 | 533 | .HorizontalList dd { 534 | display: inline-block; 535 | margin: 0; 536 | width: calc(100% - 75px); 537 | margin-bottom: 0.875rem; 538 | 539 | font-size: 1rem; 540 | word-wrap: break-word; 541 | } 542 | 543 | .HorizontalList dd:last-child { 544 | margin-bottom: 0; 545 | } 546 | 547 | .BlockList { 548 | padding: 0; 549 | list-style: none; 550 | } 551 | 552 | .HighlightList { 553 | margin-top: 0.4em; 554 | } 555 | 556 | .HighlightList > li { 557 | padding: 0.5em 0; 558 | border-top: 1px solid rgba(0,0,0,0.1); 559 | box-shadow: inset 0 1px rgba(255,255,255,0.05); 560 | } 561 | 562 | .HighlightList-large { 563 | display: block; 564 | line-height: 1.2; 565 | } 566 | 567 | .HighlightList-mini { 568 | color: rgba(255,255,255,0.8); 569 | line-height: 1; 570 | font-weight: 600; 571 | font-size: 13px; 572 | text-rendering: optimizeLegibility; 573 | display: block; 574 | margin-top: 2px; 575 | } 576 | 577 | .MetaBtn { 578 | padding: 0; 579 | border: 0; 580 | 581 | background: none; 582 | color: #3571AB; 583 | font-size: 13px; 584 | font-weight: 700; 585 | letter-spacing: 0.1em; 586 | text-decoration: none; 587 | text-transform: uppercase; 588 | text-rendering: optimizeLegibility; 589 | } 590 | 591 | .MetaBtn:hover, 592 | .MetaBtn:focus { 593 | color: #35424A; 594 | } 595 | 596 | .SkipIcon { 597 | height: 9px; 598 | } 599 | 600 | .HubIcon { 601 | position: relative; 602 | top: 1px; 603 | height: 12px; 604 | } 605 | 606 | /* Headings */ 607 | 608 | .HeadingA { 609 | margin-bottom: 1rem; 610 | 611 | color: #397BA9; 612 | font-family: proxima-nova-extra-condensed, Helvetica Neue, Helvetica, Arial, sans-serif; 613 | font-size: 3rem; 614 | font-weight: 300; 615 | line-height: 1; 616 | text-rendering: optimizeLegibility; 617 | text-transform: uppercase; 618 | } 619 | 620 | /* Formidable */ 621 | 622 | .Formidabanner { 623 | position: absolute; 624 | top: 0; 625 | right: 0; 626 | left: 0; 627 | padding: 10px; 628 | border-bottom-width: 1px; 629 | border-bottom-style: solid; 630 | border-bottom-color: rgb(29, 34, 39); 631 | color: rgb(255, 255, 255); 632 | font-weight: 700; 633 | text-decoration: none; 634 | text-align: center; 635 | background: rgb(43, 48, 59); 636 | font-size: 1rem; 637 | } 638 | 639 | .Formidabanner:hover { 640 | color: #7097b3; 641 | } 642 | 643 | .Header { 644 | padding-top: 3rem; 645 | } 646 | 647 | /* Footer */ 648 | 649 | .Footer { 650 | padding: 2rem 0; 651 | 652 | background: #2b303b; 653 | color: #fff; 654 | text-align: center; 655 | font-size: 1rem; 656 | } 657 | 658 | .Footer a { 659 | font-weight: 700; 660 | color: #FF4136; 661 | } 662 | 663 | .Footer a:hover, 664 | .Footer a:focus { 665 | color: #fff; 666 | } 667 | 668 | .FooterLogo { 669 | display: block; 670 | margin: 16px 0; 671 | } 672 | 673 | .FooterLogo img { 674 | display: block; 675 | margin: 0 auto; 676 | } 677 | -------------------------------------------------------------------------------- /img/formidable-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/part-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/react-tutorial/83331299d8ba4b327b166c9aa9038893390ed079/img/part-1.jpg -------------------------------------------------------------------------------- /img/part-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/react-tutorial/83331299d8ba4b327b166c9aa9038893390ed079/img/part-2.jpg -------------------------------------------------------------------------------- /img/part-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/react-tutorial/83331299d8ba4b327b166c9aa9038893390ed079/img/part-3.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: "Formidable Labs React Tutorial" 4 | --- 5 | 6 |
7 | {% include header.html details="true" %} 8 |
9 | 10 | {% include footer.html %} 11 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | var player; 2 | 3 | function onYouTubeIframeAPIReady() { 4 | var video = document.getElementById("player"); 5 | 6 | if (!video) { 7 | return; 8 | } 9 | 10 | var videoId = video.getAttribute("data-video-id"); 11 | 12 | player = new YT.Player('player', { 13 | videoId: videoId, 14 | events: { 15 | 'onReady': initSkipLinks 16 | } 17 | }); 18 | } 19 | 20 | var initSkipLinks = function () { 21 | var skipLinks = document.querySelectorAll('.js-skip-to'); 22 | 23 | Array.prototype.forEach.call(skipLinks, function (el) { 24 | el.addEventListener('click', function (ev) { 25 | ev.preventDefault(); 26 | 27 | skipTo(ev.target.getAttribute("data-skip-to")) 28 | }); 29 | }); 30 | }; 31 | 32 | var skipTo = function (timeCode) { 33 | var splitCode = timeCode.split(':'); 34 | 35 | // Convert hour to seconds. 36 | var seconds = parseInt(splitCode[0], 10) * 3600; 37 | // Convert minutes to seconds. 38 | seconds += parseInt(splitCode[1], 10) * 60; 39 | // Add seconds. 40 | seconds += parseInt(splitCode[2], 10); 41 | 42 | player.seekTo(seconds, true); 43 | }; 44 | 45 | var addHeadingLink = function (headingEl) { 46 | var headingContent = headingEl.innerHTML; 47 | var headingId = headingEl.id; 48 | var linkedHeading = '' + headingContent + ''; 50 | 51 | headingEl.innerHTML = linkedHeading; 52 | }; 53 | 54 | var initPageLinks = function () { 55 | var headings = document.querySelectorAll('.Primary h2[id]'); 56 | var headingContent; 57 | var headingId; 58 | var linkedHeading; 59 | 60 | Array.prototype.forEach.call(headings, function (el) { 61 | addHeadingLink(el); 62 | }); 63 | }; 64 | 65 | initPageLinks(); 66 | -------------------------------------------------------------------------------- /part-1-fundamentals.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: tutorial 3 | title: "Learn React & Flux: Part I" 4 | description: Brought to you by Formidable Labs and SeattleJS, Colin Megill walks us through Facebook's React framework in part one of this three-part series. 5 | video: Pd6Ub7Ju2RM 6 | repo: https://github.com/formidablelabs/recipes-flux 7 | resources: 8 | - 9 | title: "Thinking in React" 10 | url: "http://facebook.github.io/react/docs/thinking-in-react.html" 11 | - 12 | title: "Removing User Interface Complexity, or Why React is Awesome" 13 | url: "http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome" 14 | author: "James Long" 15 | author_url: "http://jlongster.com/" 16 | date: "May 13, 2014" 17 | --- 18 | 19 | ## Welcome 20 | 21 | **Colin**: It's intimidating to present someone else's technology that they built at their place, right? I'm not a Facebook developer; some of you know me through a startup, and some of you know me as being a dev in the community. There were a couple of questions in the comments on the meetup group, and inevitably someone brought up other frameworks and Angular, and I think I'll dovetail from that into what kicked us off tonight. 22 | 23 | ## "The Problem with Angular" 24 | 25 | I took a look today: [this is interest over time for Angular](https://www.google.com/trends/explore#q=backbonejs%2C%20reactjs%2C%20emberjs%2C%20angularjs&cmpt=q&tz=), and this is comparing it to Backbone, which is basically a non-event compared to both Angular and Ember. React is just taking off, and this is only the US, right? Whereas Angular is dominant abroad and popular in the US, React hasn't started to spread yet—it's really early days for React, but it's really exciting. 26 | 27 | I'll leave you with this quote, and we’ll use it as a way to segue into a discussion about two-way data binding versus unidirectional data flow. This is a quote {% include skipper.html time="00:01:47" %} from someone who's much smarter than me—you guys probably know quirksmode {% include skipper.html time="00:01:51" %} in the community. 28 | 29 | He wrote [this article](http://www.quirksmode.org/blog/archives/2015/01/the_problem_wit.html) a little while back, and it said this: *"In the last six months or so I talked to several prospective clients that had a problem finding front-end consultants {% include skipper.html time="00:02:00" %} in order to help their dev teams get a grip on their Angular projects. Although there are front-enders that are enthusiastic about Angular, I have the feeling that their number is surprisingly low for a major framework. I expected Angular to gain more traction than it has. Angular is aimed at corporate IT departments rather than front-enders, many of whom are turned off by its peculiar coding style, its emulation of an HTML templating system that belongs on the server instead of in the browser, and its serious and fundamental performance issues."* It's that perf issue that we're going to talk about throughout the night. 30 | 31 | This was not something that Google was touting very loudly at the outset, but now that they're having to switch to a completely different syntax, they're talking about it a little bit more: when Angular was first created almost five years ago, it was *not originally intended for developers*, which is a mind-blowing statement, because obviously it was touted as the new MVC framework. But it was targeted more at designers who needed to quickly {% include skipper.html time="00:02:56" %} build persistent HTML forms. 32 | 33 | Those organizations that have picked up Angular have run into persistent perf issues. How is front-end perf different between Angular or Ember and React? Why? At a granular level, why is this different? What's the advancement? 34 | 35 | ## The benefits of React 36 | 37 | I've been working with React for a couple of months now. I'm still beginning myself, but I'm far enough through to help you guys through first principles. That's where we're going to start. If you haven't cloned it down yet, we're going to be in the repo "react-flux-concepts" {% include hublink.html link="https://github.com/FormidableLabs/react-flux-concepts" text="React Flux Concepts" %}. Let me just zoom in on this and make sure everyone has it, because this is, like, our whole night here {% include skipper.html time="00:04:00" %}. We're going to be working through a series of progressively more complex examples. 38 | 39 | What do we need in a framework? Why do we use frameworks? In big team projects, we need convention, patterns, maintainability, performance, and more and more complex client-side state. We need increasingly complex client-side features, something that senior devs can get behind, something whole dev teams can get behind over the long term, stability in the code base, and good backing. 40 | 41 | React has hit all those, and I've seen a reaction to it in the community. It's evidenced by what we're seeing here tonight. We've never had a turnout like this for a SeattleJS meetup: we've had 430 people respond, 130 people on the waitlist, 300 accepted. It's the largest event in our history as a meetup, and I think what's wonderful about tonight is that this community comes together to learn, and that's a really great thing. We're all still growing together. 42 | 43 | All right, so without further ado, let's work through some examples. 44 | 45 | ## Hello World: the how and why of JSX 46 | 47 | If you've used React at all, you know you need a JSX transformer, and so next meetup we'll be working with a webpack build. But for tonight, we're going to do this JSX transformation in-browser. Let's look at the "hello world" example, and let's talk through what's going on. {% include hublink.html link="https://github.com/FormidableLabs/react-flux-concepts" text="002" %} {% include skipper.html time="00:06:00" %} This is JSX. It looks like HTML, but it's not HTML. It's a markup syntax that you can use to sugar functions. What's going on in React is this. You start with a … 48 | 49 | **Speaker 1**: Can you bump up the font size, please? 50 | 51 | **Colin**: Sorry about that, that's my bad. Is that big enough? Better back there in the back? Good, okay, sorry about that. This class, RecipeBook, this is called a component, and there is some new vocabulary tonight, and component is the first word. **A component is a React class, and you can define methods on it and use it to return a tree of functions that construct a DOM**. {% include skipper.html time="00:07:43" %} 52 | 53 | What React is going to do under the hood is take the JSX and turn it into JS... if you look at this on the React site {% include skipper.html time="00:08:00" %}, at the Facebook React examples, they have a little in-browser JSX transformer where you can… Bigger? 54 | 55 | **Speaker 2**: Font size. 56 | 57 | **Colin**: Font size, here you go. Where you can see what JSX turns into. Basically, it turns into a function with arguments—that div, it's turning into a function underneath the hood. React parses the UI as a tree of functions and passes it to the virtual DOM, which takes how the DOM looks and the new state, diffs them, and only repaints what's needed. 58 | 59 | This is the fundamental advancement here. We don't have to repaint strategically; we can repaint all the time. What that means is that our user interface is a constant reflection of the state of our application, of the data state, and that re-rendering allows us to use a one-directional flow versus a two-directional flow. 60 | 61 | Let's talk about this in context of a Backbone app. Let's say you have a view, and this view is listening to changes on a model, and it's setting the model, and it's getting things from the model, and they're manipulating each other. The model manipulates the view by emitting events, the view manipulates the model with getters and setters, and then you have another view over here. This view changes the model, and the model could then update and change the view over here, right? 62 | 63 | This is, like, the fundamentals of two-way data modeling, right? We're all on the same page? This is normal, {% include skipper.html time="00:10:00" %} so what's not normal is to say, **every single view in our entire application is going to be downstream of our data**. We're never going to set data; we're only going to get it. We're going to re-render the entire user interface every single time something changes. Someone presses "F" into a form input, we're re-rendering the entire application. Of course, we're not actually re-rendering; what we're actually doing is diffing the previous DOM against the new DOM and just changing the elements that need to be changed. 64 | 65 | That's the innovation under the hood here. The fact that Facebook built that means that we get to work at a level of abstraction where we can basically assume that our DOM is re-rendering every time, even though it's being re-rendered intelligently under the hood. That is, at a high level, the fundamental difference here. It makes for simpler reasoning about our applications. 66 | 67 | ## Nesting components 68 | 69 | Let's take a look at nesting. On each one of these topics now, we're going to start to have some little assignments for you, and you can try these out for a couple minutes. Let's take a look at RecipeBook. RecipeBook is acting as a kind of controller view—this is 003-nesting.html {% include hublink.html link="https://github.com/FormidableLabs/react-flux-concepts" text="003" %}—it's acting as a kind of controller view, and we're going to nest RecipeList and RecipeForm inside it. 70 | 71 | This is a "hello world" still. We're creating three components: RecipeForm, RecipeList, and RecipeBook, and we're nesting RecipeList and RecipeForm inside RecipeBook. This looks like {% include skipper.html time="00:12:00" %} magic, right? This is not familiar markup; this looks like a variable name. We have access to it because we declared it as a class. But this is not a variable name, so let's look at what JSX is doing here. 72 | 73 | If you put a couple of curly braces here, JSX is going to look for a variable. It's going to evaluate things—you can do a ternary {% include skipper.html time="00:12:36" %}, you can do maps {% include skipper.html time="00:12:38" %} and whatever else you want. I will say that the first twenty minutes or so for me, there was outright revulsion and panic, and then after that it was like okay, and then after a few hours I really didn't even notice anymore, so if you're feeling revulsion and panic, welcome. That's okay, give it five minutes. 74 | 75 | So we have nested components inside other components. What's important to note is, these two components, they're *owned* by RecipeBook. What that means is that RecipeBook is going to manage their state, and we'll see that in just a moment. But for now, go ahead and try this on your own, create a new class, put some HTML in there and let me open this up in browser so you can see. 76 | 77 | When we render this, we find that we have our top-level component, our controller view, and then we have the HTML that was returned by the functions that were nested inside. I'm going to go back and forth between calling these functions and calling them components, because really the insight here is that you're creating a user interface as a tree of functions, {% include skipper.html time="00:14:00" %} and if you can wrap your brain around that tonight, then you've really got the first principles of React. 78 | 79 | We're creating a whole user interface as a tree of functions, and we're going to be passing data down through that tree as arguments. For now, go ahead and give this a shot: create another component, nest it inside the existing component, and render something to the screen. You're going to have it return some HTML: an a tag, a p tag; if you're really adventurous you can do, like, a form. Give you a couple of minutes here. 80 | 81 | Everyone got me? If you have internet, if you finished this and it was super easy, go ahead and go to the Facebook React guide. {% include skipper.html time="00:16:00" %} Look at the JSX transform, look at what JSX becomes, and I'm going to show you up here in just a moment when we get that going. 82 | 83 | {% include skipper.html time="00:16:36" %}. Here you go. Under the hood it’s doing React.createElement and applying these attributes—they look like attributes, but they're custom tags and custom attributes—and applying them as objects as another argument {% include skipper.html time="00:17:50" %} to the function. 84 | 85 | ## Props 86 | 87 | We're going to keep going. Next let's take a look at props {% include skipper.html time="00:18:00" %}. We know we're in a function, so now we're going to think about arguments. When we define a component class, we start at the top, so we'll start with RecipeBook. When we define RecipeBook, we nest RecipeList and RecipeForm, so we've defined a new class now, and this new class is called Recipe. Recipe returns HTML that is set dynamically with data. 88 | 89 | This should look familiar to everyone who has used templating and Handlebars, yes, right? This is not a stunner that we're setting HTML dynamically, but what is a bit of a surprise is when you look at where this is coming from. This is actually coming down in what look like custom HTML attributes. Title equals "stuffed chard" in the component: we have this.props.title, so we've passed props down into two sub-components. 90 | 91 | Let's go ahead and open that up in browser—you can see we've rendered an H2 and a paragraph tag with the data that we passed in from the parent component. Now this is actually not that hard to reason about, because it's just functions. If we're thinking about this as a tree of functions, and we're thinking about passing arguments down through the tree of functions as well, we have a function inside a function, pass an argument down it through, pass an argument, {% include skipper.html time="00:20:00" %} keep passing down, keep passing down. 92 | 93 | It can go as far down as we want, and the render method of the child here simply evaluates this.props.title, inserts it, and returns that to the corresponding JSX. For this one, go ahead and check out what this.props looks like in console. 94 | 95 | this.props is an object that the child receives, and again, if you just think about this as functions, you can see past the JSX, because it looks like HTML but it's functions. The attributes—what look like HTML attributes—are basically key-value pairs getting passed down to the child. 96 | 97 | This syntax helps us reason about it a little bit more easily: we can feel like we're writing HTML. Go ahead and do the next assignment if you feel comfortable... Maybe I'll say foo equals bar, and I'll save that, and I'll refresh this, and I'll get foo: bar in the child. Go ahead and try that, and try to take one more step for yourself and get something on screen. {% include skipper.html time="00:22:00" %} 98 | 99 | You guys are being really, really good about the no-questions thing. Anyone want to throw out a question? Yeah? 100 | 101 | **Speaker 3**: Is there a way to use the autocompletion on this template? [00:22:43]? 102 | 103 | **Colin**: Like in Eclipse, so you know what you … 104 | 105 | **Speaker 3**: [inaudible 00:22:47], I can use shorthand. 106 | 107 | **Colin**: Yeah, yeah, Sublime has a JSX plugin. 108 | 109 | **Speaker 3**: Okay, cool. 110 | 111 | **Colin**: Yeah, others? 112 | 113 | **Speaker 4**: How can I do conditional logic based on value properties? 114 | 115 | **Colin**: Sure, so you can do a ternary in there, you can do anything you want in there. You can do ifs, you can do functions, you could declare a thirty-line block of {% include skipper.html time="00:23:26" %} code and have it execute. It's just got to be inside one of those curly braces. It's not best practice, though—you should put it into a method. Does that answer your question? 116 | 117 | **Speaker 4**: How to, like, alter what tag is rendered? Maybe it's an h3 or an h4 {% include skipper.html time="00:23:36" %}? 118 | 119 | **Colin**: Based on this? That's a good question. I suppose I would just make it a method, right, like methodName or {% include skipper.html time="00:24:00" %} this.three and then—sorry, that's funny, but it's really confusing. headingGenerator, there we go, that's properly abstract. Great question by the way, and then you can just do if this.props.whatever and then… Does that make sense? Great. Yeah? 120 | 121 | **Speaker 5**: [inaudible 00:24:39]. 122 | 123 | **Colin**: Good question. You can do the same for arrays. As a matter of fact, you can pass all the props down to the child, which is super handy. That is used in the router extensively. 124 | 125 | **Speaker 5**: [inaudible 00:25:20] 126 | 127 | **Colin**: Yeah, all the way in the back. 128 | 129 | **Speaker 6**: Is there any way to change the entire model? With a reference to that model, can you write to it? 130 | 131 | ## The briefest introduction to Flux 132 | 133 | **Colin**: Stay tuned, great question. So the question was, can any component change the model, right? No components change the model. Unidirectional data flow. The high-level answer to that is that here is how Flux works: any component can say, "Hey, something happened," right? {% include skipper.html time="00:26:00" %} I'll give you guys a very brief overview, and then we'll move to the next one. 134 | 135 | Any component could say, "Hey, something happened," and that fires what's called—this is a little bit of a proprietary terminology here—it fires an **action**, which is basically a callback. You fire the action, which has an action type (which is like a name: so, say, "add recipe") and a payload of whatever data changed, and you send that over. 136 | 137 | The store—which is basically the model—gets the data, gets the type, and then does whatever it needs internally to manipulate its own data... it's like closure scope, right? It's {% include skipper.html time="00:26:46" %} going to return public getters but not setters, because it's one way, not two-way, and then it's going to emit a change. And when it emits a change, the components that are listing for that say, "Oh, okay, I’ve got new data," and then you render everything, and that's the one-way flow. 138 | 139 | You re-render everything, every time. Every time you fire an action, the store changes, and everything listening to it re-renders. That's flux in a nutshell, have a good night, see you tomorrow. ;) 140 | 141 | ## Collection rendering 142 | 143 | We're going to go the next one, which is collection rendering. Obviously it's not great to declare our data in our markup; that's not realistic. We're not going to store our state in our DOM; we're not going back to jQuery here. We're not barbaric. We're civilized JavaScript developers. 144 | 145 | Let's go down to the bottom: we're going to look at this as if it is, again, a *tree of functions* rather than the markup you're seeing, and we're going to pass in window.recipeData, which is defined right above. Of course, all the components have access to this. {% include skipper.html time="00:28:00" %} We're just going to pretend it's modularized. (When we have a full repo, like we're dealing with next week, this *will* be fully modularized, and we won't have to pretend that all the components can't see window.recipeData.) 146 | 147 | So window.recipeData now has the data that we previously had written declaratively on the components themselves, on those pseudo-attributes. When we call React.render, which is our root, RecipeBook is our controller view / entry point to this tree of functions and tree of DOM nodes {% include skipper.html time="00:28:33" %} that's being generated. We say that we're going to pass in data as window.recipeData, so let's follow it through. 148 | 149 | Let's go back to our image here. We have our data, and our data is now broken out, and RecipeBook is going to receive that data and pass it down. This matches directly to the names that are in that files, by the way, so you can go back and forth. 150 | 151 | RecipeForm doesn't need this data, but RecipeList *does* need the data, so we're going to pass the props down from RecipeBook into RecipeList, and then we're going to do a map over that data. For each item in our Recipes array, we're going to create a new component, passing in the props: passing in the data as the value. 152 | 153 | We're also going to pass it the index as key, and I'm going to tell you about that in a moment, so let's stay tuned. Key is a magic word in React, and it's important for diffing. So then we have two recipes, and each has received its own data, its own props. {% include skipper.html time="00:30:00" %} Let's take a look at what that looks like in code. 154 | 155 | We have RecipeBook, our controller view. We don't pass the data to RecipeForm—it doesn't need it. We do pass it to RecipeList, which iterates over the data that's received. RecipeNodes creates the nodes with this.props.data.map, and it returns a component, which itself will return HTML—again, just think about it like we've got a tree of functions, and each one is going to return a computed tree of elements. 156 | 157 | We can then say that the title is recipe.title and the instructions are recipe.intructions, so we simply insert it like that. We grab the nodes: we've set a variable, recipeNodes, and we simply put it in curly braces, so that we tell React to look for JS. This [without curly braces] will actually write out recipeNodes, right? It's not JS; if it's in curly braces, it is JS. 158 | 159 | Let's take a look at what this looks like in the browser. We have exactly the same thing—unexciting—but it didn't break, so that's good. Stuffed chard, eggplant, and polenta: we now have data which is coming from, we'll call it a a "global truth." Sorry, we'll use the React lingo: we've got a data store. 160 | 161 | There's no assignment on this one. Basically, this shouldn't be too much of a conceptual jump {% include skipper.html time="00:32:00" %} from the previous one. Rather than passing just from RecipeBook down to RecipeList, we're abstracting the data and passing it down through arbitrarily many functions. 162 | 163 | How many people here have been in a large Backbone project? I have been in large Backbone projects. I can say that this sounds familiar: "I think we're going to need a new view." What we just did is we just increased the graph, right? We have a new view; arbitrarily many other things could be affected by this. Views are not free if you're going two-way because you're dealing with a graph, but here we're dealing with a tree. 164 | 165 | I've talked to some of the guys at Facebook, and they're telling me they're pushing like *ten thousand* components, they're pushing *ten thousand* views because everything can just be modularized, so you just keep passing data down and it keeps being okay because it’s just passing args through functions and they're not mutated. That's good news for long-term maintainability. 166 | 167 | It would seem crazy to fire that many functions every time. However, if you look through some of the React talks, the engineers at Facebook are talking about RAM versus computation on phones. For mobile, if we're going to be rendering all these DOM {% include skipper.html time="00:33:34" %} nodes over and over again, well, yeah, we've got to fire the functions, but we have like quad-core phones now. What we don't have on phones is a ton of RAM. 168 | 169 | If we're going to have mobile applications that have a ton of listeners, and that listener array is just going to grow and grow and grow, then we're taking up more and more RAM and we have to hold that state and that's RAM-intensive. That's the argument that I've heard some of the engineers make in a couple of videos, which was useful for me to help reason about it. {% include skipper.html time="00:34:00" %} 170 | 171 | We're going to move on from this to state, but before I do, I'll take maybe three questions. Yeah, Ryan? 172 | 173 | **Ryan**: What goes in render? What can and can't I do inside the render function? 174 | 175 | **Colin**: Hmm, that's kind of abstract. 176 | 177 | **Ryan**: Maybe I'm just supposed to return nodes—am I supposed to transform data {% include skipper.html time="00:34:35" %}? 178 | 179 | **Colin**: Actually, there is a really, really, critical point here: **you have to return a root node**. The root that you return cannot have a sibling. This will fail. We would need to wrap that in a parent div. And if you return a single other component, then that single other component has to return a root node, if that makes sense. I'll answer two more questions before we move on. Yeah? 180 | 181 | **Speaker 6**: Giving that key as an attribute … 182 | 183 | **Colin**: Thank you. 184 | 185 | **Speaker 6**: What is that? 186 | 187 | **Colin**: Thank you, thank you, yes. I forgot I said that I was going to come back to that. 188 | 189 | **Speaker 6**: I know it's a protected React attribute. 190 | 191 | **Colin**: It is, yes. {% include skipper.html time="00:36:00" %} 192 | 193 | **Speaker 6**: If that changes, is it going to re-render that portion of the DOM? {% include skipper.html time="00:36:03" %} 194 | 195 | **Colin**: Let's go to this really great post: [https://facebook.github.io/react/docs/reconciliation.html](https://facebook.github.io/react/docs/reconciliation.html). If you've not read it, it's called Reconciliation. Let’s read this together; it’s a super-important concept. "**React's key design decision is to make the API seem like it re-renders the whole app on every update**. This makes writing applications a lot easier but is also an incredible challenge to make it tractable. This article explains how with powerful heuristics we managed to turn a O(n3) problem into a O(n) one." 196 | 197 | You can probably guess where this is going: we make some assumptions, right, and one is that it's possible to provide a unique key for elements that's stable across different renders. That’s one of the assumptions that took it to O(n) from O(n3). The key can be the index, or it could be an {% include skipper.html time="00:37:02" %} Underscore ID, or, like, a MongoDB ID. It could be something that you hash in the function itself and just create: if it will be consistent, it doesn’t matter. All that matters is that it’s consistent across renders. 198 | 199 | Another important thing to note is that you're not getting all the performance benefits unless you use React’s PureRenderMixin, which basically says, "Yes, I’m following these two rules." By default, React assumes that you're not following its rules, which makes it not fast, so you have to do that yourself. (I’ve seen allusions to their wanting to flip that and warn you if you're not doing those two things, so it's super-performant out of the box.) If your React component's render function is pure—in other words, if it renders the same result given the same processing state—you can use this mixin for a performance boost. 200 | 201 | I won’t read this whole thing to you, but Reconciliation will explain keys. 202 | 203 | ## State 204 | 205 | Let’s move on to the concept of state {% include skipper.html time="00:38:25" %}. Let’s talk about the difference between application state and component state. If you're taking notes, this is definitely one of them. Application state we could think of as, like, the store. We’ve got a data store, which is basically our model, which our application is going to reference all the time. 206 | 207 | But that doesn’t help us if, for instance, we have a counter going, or a timer going. If we re-rendered everything, we’d just re-render that with a new value, and it's back to zero. Or if we have a dropdown, and that dropdown is open and it should stay open in between renders and we just nuked it and now it’s closed again. I typed "F" and, like, it closed—no good. 208 | 209 | So we’ve got to have **component state as well as application state**. The ideal is that there *is* no component state, that you're not using this at all, but of course you are—all the time—because you're doing things like timers and dropdowns and sorts {% include skipper.html time="00:39:34" %}. When you type, you subset a list, and you want the list to remain subsetted between renders. You don’t want the list to reflect the app truth; you want it to reflect the component truth, which may be an input someone is typing in. 210 | 211 | So we need state. {% include skipper.html time="00:39:50" %} Let’s take a look at this. This is not coming from props: this is a "hello world" in state, and it's coming from right here, in getInitialState. {% include skipper.html time="00:40:00" %} We could do something like, for instance, let’s say that we have dropdownIsOpen and we say false, right. We can return initial component state here, and this gives us a kind of local state {% include skipper.html time="00:40:27" %} that is persistent between renders. 212 | 213 | I’ll say that one more time. There is a component lifecycle, like when it gets put into the DOM and when it’s taken down. When the component mounts {% include skipper.html time="00:40:40" %}—when it goes in the DOM, right—getInitialState is {% include skipper.html time="00:40:44" %} called, and then when it comes out, it's torn down. getInitialState is called once, and then every time after that, if we want to change state, we need to call this.setState with new information. If someone clicks a button and it opens a dropdown, we are then calling this.setState({ dropdownIsOpen: true }), and then between renders it will stay that way until we say otherwise. 214 | 215 | Let’s go to the next one—we’ll linger on this one a little bit longer. Let’s open it in browser: I’ve got a makings of a beautiful dropdown. Now let’s take a look at {% include skipper.html time="00:42:00" %} this component. For simplicity, there is only one component here, and the component is recipe. When it gets put into the DOM, when it mounts, getInitialState is called, and the state is set to dropdownIsOpen: false. Whenever this callback is fired on click, we reverse it and set state. We toggle it. 216 | 217 | Let’s take a look at what this is doing. Let’s take a brief look at the page you’ll need to go to later—it's on Facebook.github.io; you can see the path here. If you want to know where that onClick is coming from and how that works, this is the article to read. We can put an onClick onto our component, but remember that’s one of these magical pseudo attributes, right? 218 | 219 | We're passing it down as a prop, as an argument. In fact, **we're passing a callback into a function as an arg**: {% include skipper.html time="00:43:37" %} not so mysterious. Looks weird in JSX, but not so mysterious when we map it like that. When the click is fired, it does this.handleClick, and handleClick toggles the state. {% include skipper.html time="00:44:00" %} Then, in this function here, this.state.dropDownIsOpen: if it's not there, render it; if it is, don’t. 220 | 221 | Go ahead, there are some instructions here. Add a hover event, and when hover is true, change something in the DOM—background color or something like that. Yes? 222 | 223 | **Speaker 6**: If you have an external library and it relies on jQuery {% include skipper.html time="00:44:46" %}, can you use it with React? {% include skipper.html time="00:44:48" %} 224 | 225 | **Colin**: Could you be more specific? 226 | 227 | **Speaker 6**: Let’s say you have a third-party library {% include skipper.html time="00:45:02" %} that’s dependent upon jQuery: {% include skipper.html time="00:45:05" %} could you still use it? 228 | 229 | **Colin**: Yeah, well, so... We're probably about to rewrite everything in components. [Laughs] Hey, it'll be fast... 230 | 231 | You could also, a simpler version of this. If you want something that only takes a minute, change this.state on hover and console log this.state {% include skipper.html time="00:46:00" %} and just see what comes up in the console. See, you're changing state and toggling it back and forth. 232 | 233 | **Speaker 7**: How is event bubbling happening? {% include skipper.html time="00:46:14" %} 234 | 235 | **Colin**: Man, that’s a great question. Can someone from Facebook handle that? Is there anyone in the room from Facebook who can handle how bubbling is done in React? Because I do not know. I’m just going to put that out there. I can absolutely come back to you with that in the next meetup, but I don’t know. I’m going to take a note. I have a feeling that that’s a research project for me, like how it would contrast with what we’re used to doing in other systems. But I've kind of just done it, and it’s just worked up until this point. 236 | 237 | **Speaker 8**: Does the event return through if handled? Is there a true/false thing that handles bubbling? 238 | 239 | **Colin**: I don't know. It’s a good question. 240 | 241 | **Ryan**: Chat room says no bubbles. 242 | 243 | **Colin**: Chat room says no bubbles. {% include skipper.html time="00:47:34" %} This is a cross-browser wrapper around the browser’s native {% include skipper.html time="00:47:43" %} event. The event handlers below are triggered by an event in the bubbling phase. To register an event handler {% include skipper.html time="00:48:00" %} for the capture phase, append "capture" to the event name. All right, I think it’s a research project to compare and contrast with jQuery—sorry. {% include skipper.html time="00:48:07" %} 244 | 245 | **Speaker 8**: Well there's a boolean in there {% include skipper.html time="00:48:12" %} for "bubbles," [inaudible 00:48:14] whether or not it happens in the bubble page. 246 | 247 | **Colin**: Is that what you're pointing towards? {% include skipper.html time="00:48:26" %} 248 | 249 | **Speaker 8**: [inaudible 00:48:30]. 250 | 251 | **Colin**: Oh, aha, yeah, there you go. 252 | 253 | **Speaker 8**: But it sounds like if you wanted to do capture phase, you have to do a little more work. 254 | 255 | **Colin**: Cool. Give me maybe a minute and half, two minutes more on this one. See if you can console.log out state given an event. Thanks. {% include skipper.html time="00:50:00" %} Thank you {% include skipper.html time="00:50:11" %} for the first PR. Please feel free to PR corrections, improvements, other ideas, other steps, intermediate steps, all welcome. This is a public repo, so if you think you’ve got a great idea for an intermediate step, PR. 256 | 257 | **Speaker 9**: Like no hard {% include skipper.html time="00:50:32" %} tabs. 258 | 259 | **Colin**: What’s that? 260 | 261 | **Speaker 9**: Like no mixed {% include skipper.html time="00:50:34" %} tabs. 262 | 263 | **Colin**: No mixed tabs, thank you. I may have finished it at 4 am {% include skipper.html time="00:50:41" %}. Before we go on to Flux and application state, let’s take a breather for a couple of minutes here. Address any high-level {% include skipper.html time="00:51:10" %} questions before we move on. Yeah? 264 | 265 | **Speaker 10**: {% include skipper.html time="00:51:20" %}. The key uses [inaudible] {% include skipper.html time="00:51:29" %} 266 | 267 | **Colin**: Yeah, not the best key. 268 | 269 | **Speaker 10**: Exactly. 270 | 271 | **Colin**: If you're guaranteed that you're only going to be pushing onto the array in your store, I’d use a GUID {% include skipper.html time="00:51:49" %}. Yep? 272 | 273 | **Speaker 11**: ...if you have a list of items or every item is selectable. 274 | 275 | **Colin**: Is what? 276 | 277 | **Speaker 11**: Selectable. There is a checkbox and you can [inaudible 00:52:00]. {% include skipper.html time="00:52:00" %} Where would you [inaudible 00:52:03] on the list, or on the item? 278 | 279 | **Colin**: Great question. You passed, so if you need … 280 | 281 | **Speaker 12**: Can you repeat the question? 282 | 283 | **Colin**: Yeah, if you have a list—UI with a bunch of lis in it—and every single one of them is selectable, where do you keep the state? It’s going to be in the li. I don't know another way to do it, because each li is going to have to handle its own state. That’s like a lowest level of concern. Cool. By the way, if I’m egregiously wrong about anything, I absolutely will find out because I’m in front of 300 {% include skipper.html time="00:52:55" %} people. And guess what, I’ll let you know. 284 | 285 | ## Flux 286 | 287 | Let’s move on to Flux. At a high level, this is going to be a bit of a ride for those of you who have only done object-oriented programming and have never done functional. The concept of immutable data is frustrating because pretty much every time, you're going to want to, like, touch the data and do the thing. You're just like, "It's right there, {% include skipper.html time="00:53:29" %} that data is right there, it’s an object, I just want to push it, move it, and manipulate it," and you can’t. The reason you can’t is because the data is immutable. 288 | 289 | One of the things this gets us is a guarantee that our UI is current, because the data is always current. If we have that guarantee, if we know nothing has changed, we just re-render the UI based on that data. Nothing is hiding anywhere else. I’m going to talk about it {% include skipper.html time="00:54:00" %} at a high level verbally, I’m going to walk through an image, and then we're going to walk through a file. 290 | 291 | At a high level, there are some new vocabulary words, which are *component* and *action*. An action is fired by a component. Flux could have probably been better named "a bunch of callbacks with some organization and a subscription pattern." That would be my longer name for it. {% include skipper.html time="00:54:28" %} It’s not actually that much. We fire a method on our component, and it fires an action, and that action has a type, and that type is a string like ADD_RECIPE, for instance. 292 | 293 | Then we have a string and we have a payload, and that payload is just JSON— {% include skipper.html time="00:54:53" %} arbitrarily large amounts of JSON {% include skipper.html time="00:54:58" %}. That's passed to all the stores that are listening, and the stores—actually, the stores are listening to *all* actions. Anytime an action fires, all the stores get it and they run a bunch of ifs, like, "Do I have it?" "Do I need to know about this?" If they need to know about it, then they run some logic internally, manipulate their state, and say, "Hey, I changed." At which point a change is emitted. 294 | 295 | The controller view is listening for application state and says, "A change was detected." It uses a getter—the public getter on the store—to say, okay, what is it now? It goes and gets the data, and then it passes that through props down to all its children. We just pass all that data down through the tree. Again, if a store is changing on every letter someone is typing, that doesn’t mean we're doing an AJAX call; it *does* mean the tree of functions is firing. 296 | 297 | Every letter someone types, the store is changing; every letter someone types, {% include skipper.html time="00:56:00" %} we're then re-rendering our view. We're passing that data down through props. 298 | 299 | At first glance, this seems inefficient, but it’s the diffing engine that saves us. Let’s take a look at a diagram. Let’s say we're handling that typing event. We have an onKeyUp event in recipes, and on key up, we're going to fire an event that’s, like, "Recipe input modified." It fires an action that has the new data: whatever the text was, whatever the content in that form input was. It says, "Hey, here's an action of this type," and then an action of this type is fired, and who's listening? 300 | 301 | All the stores are listening; RecipeStore is listening. The store gets that data and gets that action type. It fires whichever internal methods {% include skipper.html time="00:57:08" %} that it has to to manipulate the data. Then it fires the change, and that’s actually because we're using a little wrapper. It’s basically a flux factory: it’s a little generator called McFly, which was written by Ken Wheeler. 302 | 303 | That change is fired, and the recipe controller subscribes to that. Now the recipe controller fires its method. Then there's one more line here between the recipe controller and the public getter of the store itself. It’s going to call a public method on the store saying, "What’s your data now?" Then it’s going to re-render {% include skipper.html time="00:58:00" %} the entire tree of functions, passing all that data down wherever it needs to go as props to Recipes, which then re-renders, and now the data is on the screen. 304 | 305 | We’ve got to go through a lot of steps just to get, like, a single character on the screen. However, we know that it’s consistent with our data, so that decreases complexity in the long run. But certainly, at first glance, it’s like "Whoa, that’s a lot of steps per user action." 306 | 307 | I should clarify, what I’m talking about in that case is that controlled input {% include skipper.html time="00:58:35" %}. A controlled input is when you're setting the value of an input programmatically from your data, which you don’t have to do but you can do. The first example of Flux does not do that, subsequent examples of Flux do that, and then your homework for the whole event does that. 308 | 309 | We’ve got 45 minutes just to talk this through. Let’s start with Flux, and let’s take a look. {% include skipper.html time="00:58:57" %} I’m going to open this in browser—this is 006-flux {% include hublink.html link="https://github.com/FormidableLabs/react-flux-concepts" text="006" %}—and I’m going to add a recipe. I add a recipe and there it goes. 310 | 311 | Now that we’ve talked it through at a high level, I’m going to go through the actual code {% include skipper.html time="00:59:17" %} and reference actual methods. Let’s start with what we know. We have a Recipe component, and we have a method on it called addRecipe. When that’s called, we're going to fire an action called addRecipe, and here is the data that we're going to send. We're just going to send a randomly generated ID. 312 | 313 | Render is itself going to return a map of existing ingredients and the button. On click, we're going to just add a new recipe. It's a trivial feature—we can’t edit, we can’t {% include skipper.html time="01:00:00" %} delete. We're just going to create a recipe and generate a GUID for it. {% include skipper.html time="01:00:07" %} This is, like, our most basic flux todo, just to show data going through the system. 314 | 315 | So, going through: we click this.addRecipe, and this.addRecipe fires RecipeActions.addRecipe. Wait, what's that? All right. Up here at the top, {% include skipper.html time="01:00:24" %} let’s take a look at flux. We’ve instantiated flux, and we've then used Flux.createActions, the little factory here. Here is our first one, addRecipe, and it takes text and returns action type, addRecipe and text, text. That is what our store is going to see. In our store, when you do flux.createStore, under the hood McFly is automatically fusing these things together. 316 | 317 | You can look at a number of different implementations of Flux—the core similarity is that they use something that Facebook released called the Dispatcher, which is a registry for callbacks. But that’s under the hood for us here. Ken told me he wrote this in a day. You guys will have your pick of flux implmentations: {% include skipper.html time="01:01:29" %} there are like eight now, this will {% include skipper.html time="01:01:32" %} just grow and grow, there's not much to it, so you might even consider building your own wrappers to make it behave the way you want. 318 | 319 | We pass in a callback. This is our registry, so anytime any action is fired, {% include skipper.html time="01:02:00" %} this callback is also going to fire. The action type is going to be passed in, and so is our payload, which is our data. If the action type is addRecipe, then we're going to fire addRecipe: payload.text and we're going to emitChange and say, "Hey, we changed." 320 | 321 | What is addRecipe? addRecipe is simply recipes.push(text), and this guy right here, that’s the store. It’s a private variable, which is an empty array. {% include skipper.html time="01:02:37" %} It could be a model, I suppose; it’s a collection, basically. I suppose if you had a config, {% include skipper.html time="01:02:46" %} maybe you would have an object and you’d just use different underscore or lodash methods to manipulate it, but I guess I've only seen them as arrays. {% include skipper.html time="01:02:59" %} It's just data. 322 | 323 | Let’s talk through how we might approach the assignment and then I’ll go through this in a couple of different ways. We're going to add a new button that has a new callback that fires a new action. We're going to update the store with the new data, and then we're going to log out the value from the component's render method to make sure the data made the trip back to the component. Let’s go through that, which basically takes you through the entire flux flow. 324 | 325 | So, our button was down here; maybe this new one is a button that, on click, fires this.NewMethod. You're going to define a new Method here. That’s going to fire newMethod, right, {% include skipper.html time="01:04:00" %} and that’s going to fire "SOME NEW ACTION" and it’s going to send some text—it could just be a string, right. It could do like RecipeActions.someNewRecipeAction("kittens"), right. Launch the kittens around flux. Then we’ll need to create that action, so in addition to addRecipe, we’ll have to add a new action. 326 | 327 | Again, this text here is just proxying through, right. Whatever that method on the component fired, you're proxying that straight through to the store. Just text becoming text. Add a new action, and then listen to that in the store, and then maybe you can implement, say, a delete. {% include skipper.html time="01:05:05" %} If you click on the button, it deletes everything. You could do something like, if payload.actionType === "NUKE_STORE," then you’ve got deleteRecipes, and deleteRecipes could be a method that just, like, sets the store back to an empty array. That would be an easy button to implement. 328 | 329 | Then let’s take a look at our controller view. How does our controller view know that the store changed? Well, we include RecipeStore.mixin in the controller view, and that—through the dispatcher—sets the component up to listen for any store changes. Again, that’s under the hood. {% include skipper.html time="01:06:00" %} We're just focusing on data flow here, and we won’t go down to that next level of abstraction. 330 | 331 | It would be good to talk about what a mixin is. Facebook seems to consider it an anti-pattern—the people I've talked to {% include skipper.html time="01:06:16" %} consider it an anti-pattern—because you can just define additional functionality. Basically a mixin just puts additional methods on the component. You could define a module that had a bunch of cross-cutting methods, and you could have a mixin that's applied to multiple components that need those cross-cutting methods. They're just applied to this object. We're passing in an object when we create the class, and, through a mixin, that object can have another key-value pair [method name / function]. 332 | 333 | This mixin sets up the listener, the controller view will listen to the store, and that's the ballgame. {% include skipper.html time="01:07:12" %} Once it knows the store changed, the whole thing re-renders with new data passed down and the diff sees what’s new, renders what’s new, and the data is on screen. 334 | 335 | That was a lot. How many people feel like they could tackle this right now and they might be okay? Do it, awesome. 336 | 337 | ## Q&A 338 | 339 | I’m going to give you a minute or two of silence here, and then I’ll take a few more questions. That’s great. Well done, everyone {% include skipper.html time="01:07:45" %}. 340 | 341 | {% include skipper.html time="01:13:28" %} I’m going to take a few questions now that you've had a second to look at this and catch up, and I’ll go over some of these basic flux concepts over the next 20 minutes or so, and then I’ll leave you with flux enlightenment as your koan until we meet again. 342 | 343 | **Speaker 13**: Why do you like McFly over other flux implementations? 344 | 345 | [Edit 4/21: Flux implementations have continued to proliferate, but some important influencers are pointing to [Flummox](http://acdlite.github.io/flummox)] 346 | 347 | **Colin**: I didn’t initially; I really liked reflux and I talked to {% include skipper.html time="01:14:00" %} {% include skipper.html time="01:14:00" %} Bill Fisher, and he was like, that’s not a flux app. I was like okay, we'll do McFly. Basically, Bill Fisher blessed McFly {% include skipper.html time="01:14:08" %} and I was like, well, if we're going to have this on video, we'll use the one that he likes. Ken Wheeler, who built McFly—I was like, "Hey, could you port this [demo app] for me from reflux?," {% include skipper.html time="01:14:20" %}, and it took him like 40 minutes. I think we're going to see a lot more come out that sugar these ways of moving data around. 348 | 349 | Hold on, let me show you something. Fluxxor is another one implementation. Let’s see, documentation... {% include skipper.html time="01:14:53" %} I found this one a little bit verbose, {% include skipper.html time="01:15:13" %} but it’s really personal preference. I think that McFly is nice, it’s minimal, there isn’t much to flux and there isn’t much to McFly either. It’s clean. I thought that reflex couples things more tightly, makes things a little more automatic, and I think for small apps it might be the faster to write, but then when I got into McFly, it was like "Eh, well really that wasn’t that much slower." 350 | 351 | [Edit from Colin: though I did this port at the last minute without thinking it through, Bill Fisher was totally right. Here's why: In reflux, the store knows what data it needs to send to the components. That's all wrong, and it's not flux. It couples the stores too tightly to the components. It _did_ help me learn quickly by decreasing initial complexity, for what that's worth.] 352 | 353 | It’s really early days, and I’ll say this too. Facebook just had its {% include skipper.html time="01:16:00" %} React Conference, and there were a whole bunch of presentations about what’s to come. One of the things that I just don’t know enough about yet, because they haven't released it yet, is Relay and GraphQL. But they did release the talk on it. I can’t speak to it because it’s not released yet, but there's more coming. 354 | 355 | I don't know how portable Relay is going to be from Facebook's stack. {% include skipper.html time="01:16:38" %} I’m sure they're conscious of that and working on it. The concept behind Relay is that the components declaratively say, "This is the data I need," and then behind the scenes that data comes to them without passing, without relying so much on props. The logic there being that you’re actually leaking implementation details to the parent with props. The parent needs to know that the child needs that data. It doesn’t bother me so much, but if you have 10,000 components then I can see how it would. 356 | 357 | Cool. Other questions? Let’s take a look at the other one. I have Kitten 1, Kitten 2, and I can delete them. Now we’ve bumped up to CRUD functionality. Actually there is no update—it’s just create, read, and delete—and then {% include skipper.html time="01:18:00" %} you can clear them all. 358 | 359 | I’m actually going to give you guys a moment. Just take a look at this one. Take a look at flux form, and pay specific attention to deleteRecipe. See if it's like what you were thinking. {% include skipper.html time="01:18:19" %} By the way, just as an aside, how many of you got that data going around? Nice, maybe half, cool. Let's take a look at the different action types. Now the recipe store is listening to multiple action types. The component is firing multiple action types. Compare this with your implementation. 360 | 361 | You’ve got another button firing another method, which fires another action with a different payload. The store listens to that, listens for a clearRecipes action, and then mutates its data. Again, just like before: window.data wasn’t private, and eventually _recipes would be modularized and not returned for anyone else manipulate. 362 | 363 | But in the meantime, we have to pretend everyone else can't see it. The methods in the store, like addRecipes, clearRecipes, and deleteRecipe, manipulate the data in the store and then emitChange. That change goes through RecipeBook, where getInitialState {% include skipper.html time="01:20:00" %} calls getRecipes. 364 | 365 | What does getRecipes do? getRecipes is our public getter on the store. RecipeStore.getRecipes just returns _recipes. It just returns that array and we can get the data. There's no problem with getting the data. The problem is mutating the data from the component. That’s what we don’t do. 366 | 367 | Again, just to reiterate, that’s what you're going to want to do *all the time*. It's what I wanted to do all the time when I got this. It was like, {% include skipper.html time="01:20:36" %}, "I’ll just go over and hit a brick wall because I can't touch that." {% include skipper.html time="01:20:39" %} We can’t mutate the data unless we fire an action. 368 | 369 | Let’s see. Once again, you have a couple of buttons here, so we can see how that compares to your implementation. You’ve got another button that fires another action with another payload. The store mutates itself, fires a change event, and the controller view (which is listening) re-renders and passes its props down. Now let’s break that. Now, if we look at 006.2, {% include hublink.html link="https://github.com/FormidableLabs/react-flux-concepts" text="006.2" %} we can no longer type. Take a look and see if you can figure out why. 370 | 371 | **Speaker 14**: It's re-rendering? {% include skipper.html time="01:21:51" %} 372 | 373 | **Colin**: Well... That’s not the answer. It's the right direction though. Yeah? 374 | 375 | **Speaker 14**: You don't send the action on keyup [inaudible 01:22:06]. 376 | 377 | **Colin**: Yes, that’s true; we're not doing that. That’s not the specific reason we don’t see anything and nothing changes, though. Yes? **The value is set to an empty string**. That's right. If we set the value to "kittens," we're going to get "kittens." Let’s go down here to the input and set that input value to "kittens." We save that, we refresh it, and we cannot get those kitten out of there no matter how hard we try, and they're so adorable. 378 | 379 | That is homework. I’d say this was the moment when I really understood what was going on here. Wait a second, why can’t we type? We're setting value programmatically. Oh, we're setting value programmatically when there's a key event, we set an action and we're actually re-rendering. It’s going to send that "P" or that "F" or that "K" around into the store and update the data. It's helpful to think about creating things implicitly: if someone shows up on New Recipe, you create a recipe model, basically a blank one right when they show up. It’s helpful to think about that here. Yeah? 380 | 381 | **Speaker 15**: [inaudible 01:23:37]. 382 | 383 | **Colin**: Nope. Nope, it’s because we're setting value and… What do we need to be setting value to? 384 | 385 | **Speaker 16**: State. {% include skipper.html time="01:23:51" %} 386 | 387 | **Colin**: Yep, you could set the value to state. That's right. [Edit: or, really, props.] Your homework between now and the next class {% include skipper.html time="01:24:00" %} is to try to solve this little problem here, try to fix the form functionality. Make the inputs a subcomponent. This is in 006.3, "flux enlightenment." Fix the form functionality, make the input a subcomponent, and pass it a method from the parent that fires an action with the form instance data. The form is going to have data, and we’re going to want to get the data out of the form. 388 | 389 | Let me show you the DOM, that’s what this is. this.refs: we’ll talk about refs in like 30 seconds after I give you homework. this.refs.inv.getDOMNode.value is going to return a reference to the actual DOM node and its value, so this would be like your $("#someId"). If you're looking, when you see ref, think this use case—or text, right. When you want to do that, you should head toward refs. Let’s go back to step 3 {% include skipper.html time="01:25:25" %} here. 390 | 391 | We want to re-render the input every time the store data updates with the new value from the props. We're going to have to pass the value through the store. Now everything in the UI is a reflection of the store. [UI is data and data is UI.](http://appamada.pbworks.com/f/Heart%20Sutra-Red%20Pine.pdf) That’s homework. That's the bridge between all these examples and the full repo. Next time, we're going to be in a full repo with a server and API calls and more flux and a webpack build and all the rest. 392 | 393 | I’ll point you specifically to the place in the recipe app that we’ll be going over all of next week. We’ll do a review of all this, and then dive into that app. I’ll point toward {% include hublink.html link="https://github.com/FormidableLabs/recipes-flux/blob/master/client/components/ingredient-form.jsx#L27" text="the place in the app where the switch happens" %}, and where you see this happen. Angela is hard at work cleaning this up, so thank you, Angela. 394 | 395 | For extra credit, there's a problem, and that problem is when you re-render a form input and you press backspace. Let’s say you're in the text area, and you're rendering that text area every time, and it’s great... but a user sees something in the middle and they go back and they say, “Okay, I want to edit this part of the paragraph.” They start hitting backspace, they hit backspace once. Where does the cursor go? It goes back to the end because it’s new again. 396 | 397 | So for extra credit, {% include skipper.html time="01:27:27" %} fix that too. There's an issue on the React repo that tells you how. Somewhere had asked about why it's 100k {% include skipper.html time="01:27:37" %}—I’m guessing that’s why React is 100k. 100k isn’t a ton, but Riot {% include skipper.html time="01:27:41" %} is like, “We're 5k.” It may have everything in there, but I’ll bet you there are a bunch of edge cases like this that Riot {% include skipper.html time="01:27:48" %} doesn’t cover yet. I’m guessing, I don't actually know that, but this edge case is handled in React. 398 | 399 | With that, we're going to start… If you want to on your own, you can look at the router. Actually, that’s enough for tonight, we're not going to go into the router, but we’ll look at 10 and 10.1 starting next time. At this point I’ll take questions and say, "Thank you." -------------------------------------------------------------------------------- /part-2-building-an-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: tutorial 3 | title: "Learn React & Flux: Part II" 4 | description: Brought to you by Formidable Labs and SeattleJS, Colin Megill (@colinmegill) walks us through Facebook's React framework in part two of this three-part series. 5 | video: iR22EWW-CVc 6 | repo: https://github.com/formidablelabs/recipes-flux 7 | --- 8 | 9 | ## Review 10 | 11 | **Colin**: Okay, we're going to start out with a brief review. Thank you for packing the room a second time. Also, I'll say this is the first thing, you cannot believe how exciting the last one is going to be, and I don't say that sarcastically, the last one is going to blow your mind [Edit: See [Radium](http://projects.formidablelabs.com/radium/), discussed in part 3 of this series.] Don't miss it. 12 | 13 | We're going to talk a whole bunch more about Flux. When we talked last time, we talked about components. We talked about React, we talked about rendering, diffing, passing data around and we just left off at the end with Flux, but we didn't talk about React router. We're going to start with that tonight. We didn't talk about Web Pack for build and we didn't talk about Flux in the contents of a major project. Major as in non-trivial, not like just a todo {% include skipper.html time="00:01:11" %}. That's tonight. 14 | 15 | Tonight is going to be a little bit more open-ended, because of that it will probably last ... Last time we were actually an hour and a half sharp, and tonight it will probably go two hours. Because we've got a little bit more open-ended collaborate stuff. We're going to enable a feature, you guys are going to pair up, and implement features on your own. Without further ado, let's do some work. 16 | 17 | Okay. react-flux-concepts {% include skipper.html time="00:01:43" %} {% include hublink.html link="https://github.com/FormidableLabs/react-flux-concepts" text="react-flux-concepts" %}. If you could go ahead and open up the repo that you have from last time, which is Reacts Flux Concepts, and oh, maybe there are some of you who do not have that. If you don't have it it's here, reacts-flux-concepts {% include skipper.html time="00:02:00" %}. It's Formidible Labs, same place you just went to for the other one, Formidible Labs React Flux Concepts. We're just going to walk briefly through what we did last time just to wake our brains up. We started out, I'm going to make the text way bigger. Okay, we started out in an empty project. Everyone in the back is this big enough? 18 | 19 | Students: Yeah. 20 | 21 | **Colin**: Cool. All right. We started out in an empty project, and then we get a hello world going by instantiating a class, right. React.createClass and passing in a bunch of methods, the most important of which was render because that returned some JSX. That JSX was parsed and became just basically a function, right. Then render returns this HTML, it's basically a tree of functions that represent the DOM and that gets passed into react and goes through the diffing engine under the hood. Then we call react.render we pass in our component and we pass in an element and this is the basics of a hello world. 22 | 23 | Then we talked about nesting components and when we nested components we instantiated RecipeList and RecipeForm and we would define them. Then we would instantiate them inside of another component. RecipeBook then becomes a controller view, and the controller view is responsible for the data of his children. So you're passing data down from RecipeBook into RecipeList and RecipeForm. 24 | 25 | Then we talked about that. How do you pass data from a parent into a child {% include skipper.html time="00:04:00" %} and so we'll look at recipe book and that instantiates RecipeBooks to RecipeForm. RecipeList instantiates two Recipes, so now we're nesting triply, right. We pass in title and instructions as they look like HTML attributes, but they're not. They actually more like arguments to functions. They're available on an object this.props and you get a correspondence here between in Recipe. You can see this.props.title and this.props.instuctions are available in the child component. We talk about the RecipeList as the owner of those two recipe children. 26 | 27 | Okay, so then we abstracted that so it was into an array and we took the Windows.recipeData and now rather than storing the data in those HTML attributes, which looking like looking things right in props. Of course we're not going to have data like that, we're going to have data coming back from an API. We looked at what it would look like to map over an array. 28 | 29 | Let's follow this through. Once again, same thing in RecipeBook, we instantiated two components and RecipeList we passed in this.props.data. You can see we passed in to our controller view, our very top level view. We passed in Window.RecipeData, then data = Windows.RecipeData. Then this.props.data is going to be available, right. This.props.data will pass that though to its child, so the controllerView, which is going and getting data, is passing the data into all of its children. Remember that we talked about this as tree of functions. This is basically just arguments. You're passing data down through arguments, or down through a tree of functions as arguments. 30 | 31 | Then if we look {% include skipper.html time="00:06:00" %} in RecipeList, RecipeNodes is this.props.data. So same thing this.props.data we have access to that. Which came from the parent components in the tree, its a tree of components. We're going to do a map, the native {% include skipper.html time="00:06:18" %} array method over that array that is coming down. We're going for each item in that array. We're going to instantiate Recipe, right. We're going to pass it props. Recipe.title, Recipe.instructions, and the this.props.title. This.props.instructions is going to spit them out on the page. 32 | 33 | I think I can open this in browser and show you what that looks like. Yeah, here you go. We've now created a list out of an array. That is a basic collection rendering. Then we moved on to state. State is, let's see, let's go to the drop down. Yeah, here we go. 34 | 35 | The ideal would be that you don't have any state your components. The components are stateless and they are always rendering from a source of data and they are just a reflection of that. That's not always realistic, because obviously your drop down menu is going to stay open or maybe there are lists that are subsetted. Even if there updating from data, you need to make sure that they're in sync. Sorry, so that the list items that are subsetted are persistent, even though other parts of the app are going to rerender. 36 | 37 | We talked about state, and so I'll show you what this one looks like and then I'll talk about it. Super simple, you click it, you get a drop down, you click it, you get a drop down, right. We made that work by using getInitialState {% include skipper.html time="00:08:00" %} and we made the component stateful. This state will perpetuate between renders, so that you're not nuking {% include skipper.html time="00:08:13" %} all state every time you're rendering, although you are receiving new props every time the component renders. Let's see. This ternary {% include skipper.html time="00:08:00" %} right here is this.state.drop is swapping them. Every time it's clicked, it does this.setState and that's how we interact with state. You don't want to interact with state, you can read state, but you don't want to mutate state unless you're calling this.setState. 38 | 39 | Then we got in the Flux, into the basis of Flux. We use the McFly library which is a little factory generator, very small on top of flux that just gives us some convenience methods that are nice for the meet up. Makes it a little bit faster than working with the raw dispatcher, but that's fine too if you want to just require into your project Facebook Flux and roll your own. This is a hard topic to talk about right now, because the ecosystem is changing very very rapidly. Even between the last meet up and this one, I've had conversations and read things that have said, oh maybe I can do it that way. There are just a ton of right answers, because it's a very minimal pattern. 40 | 41 | We're going to dive in tonight and I'm going to show you what I did. I'm going to show you what I did in an app. I've talked to a couple framework creators and got their perspectives on it. {% include skipper.html time="00:10:00" %} talked to some people at Facebook about how they were doing it, kind of got a perspective and this was the app that I built. We're going to go over it. Just know that you're going to read articles that are going to say really different things and that's where the ecosystem is right now. I'll try to give you an overview of what some of the different ways that you could go are. 42 | 43 | Let's take a brief look at Flux before we dive into the router, and talk just briefly about the homework. An overview of Flux. Let me grab, actually let me start with this because this will be ... We'll do it visually first, then we'll do it in code. Okay, so remember that we have a RecipesController view and the RecipesController passes props down to it' children. You have this tree of components, which is this tree of functions that all return representations of the DOM written in JSX, that then build your HTML. When the user does something in the app it's going to fire an action, this is the basis of Flux. It's going to fire and action, and that action is going to trigger a change in the store. All of the stores are subscribing to actions. We're going to see this in code. I'll show you this again in code in just a moment. 44 | 45 | The user does something, that fire in the component in a form on a button or whatever, let's say it's a button. They fire that and it says action type button flicked and the stores are all listening. Every store is listening to every action {% include skipper.html time="00:12:00" %}. If the store cares about that it's like, oh I care about button flicked, so I'm going to take in some data, change my state and once I've successfully changed state, I'm going to call emitChange. Once I call emitChange any of the components which need to know about a change in that store, will then rerender with new props and pass those props down to their children. Then that circle continues. 46 | 47 | What does this not do? Well, if you are used to backbone or two-way data binding in general, what will look unfamiliar is that your RecipesController and you Recipes they can get but they cannot set data. You never call this.model.set some data. You are never changing the data from your view. The view just says something happened and here's the data associated with it. He doesn't know if there's one store out there or if there's sixty stores out there. It doesn't know and it doesn't care. Similarly when the store mutates all it knows is, I took data in and it was of this actions type and I fired these functions and mutate my state. I am now done, I emit change. It doesn't know whether there's one component that wants to do that or sixty, it just fires in it's change and then all of the components update themselves. 48 | 49 | That can be as granular as every time someone hits a button into a form. That whole loop could occur and the idea being that your user interface is always a representation of your data. You don't have parts of your app that are in a different state that are holding their own state and then that state is going to be passed up to parents, which are they are going to pass it to other children. Which is if you did backbone, that's the way you do it. 50 | 51 | Lets' take a look at that in code now. We instantiate the Flux factory. Under the hood, this is using Facebook's {% include skipper.html time="00:14:00" %} dispatcher. Under the hood McFly is using Facebook's dispatcher and wiring up the stores and the actions and the components when you pass the mixins into the components. The RecipeStore is just data, it could be an object, it could be an array, it's typically a collection, right? It could be an object. Function addRecipes is a little utility that is going to push a new string into our Recipes array. I'm going to open this really quick and show you what this does, because it's not a whole lot, right. That's all this does. It pushes a random number into a list. That's all it does. 52 | 53 | We use our little McFly as our Flux factory. We use the Flux factor to create a store and the store has a single method, which is getRecipes, which it's going to return to the component and it's listing for an action. Which is an action type addRecipe, and so we define that action here. RecipeActions and there's a single, there's just proxies text through and actionType is addRecipe, right. Then we're going to fire that from our RecipesController. OnChange we're going to setState to getRecipes. Here is our public getter. It is up here. It's returned from the store which is in one file. Soon this will be modularized and you'll actually have to return these things, but since this is all basically global since this is an HTML file we don't see that right now. {% include skipper.html time="00:16:00" %} 54 | 55 | Let's take a look at this. RecipeActions.addRecipe this is the part that I really want you to focus on now, because this will help make sense of everything you are about to see when we get into the bigger repo. When we get into the RecipesFlux repo. This is the moment when data enters the system. I find this to be a really helpful place to focus on, because the moment the data enters the system is the moment you have to start handling it right. 56 | 57 | So button.onClick, remember when I clicked the button we're putting a random number into the list, so what's the data flow. So onClick this.addRecipe and this is in the component. This is in the Recipes component. What does addRecipe do? AddRecipe fires an action, right. RecipeActions.addRecipe so when we click the button we fire a method on the component. The component fires an action, and that action sends data. See addRecipe, it sends an id which is a random number. That's all it does. 58 | 59 | Now we've got new data in the system. What is that action? Let's go back up to that action. The action, so now remember we've got a random number as the text, right. The random number is now going to be able find the action. The random number is going to go, first it's going to be absent as an arg, right. It's going to get assigned to text and the action is going to become two things, data and a type. The type is ADD_RECIPE and the text is just text. The text is just the data that we passed in. Now let's look at the store. In payload.actionType {% include skipper.html time="00:18:00" %} what you pass it in, remember all the stores are listening to all of the actions. Any time an action is fired all of the stores callbacks are going to fire, and the payload is going to get passed in, and we're going to see payload.actionType, right. That's down here, that's addRecipes so it's going to return true and addRecipe it will now will call our addRecipe. That's our method that's going to mutate our store. addRecipe(payload.text) that's our random number and then we emitChange. 60 | 61 | When we emitChange the RecipeStore.mixin is going to, getRecipes let's see, there you go so. When emitChange is fired the RecipesController is, basically the components which need to know that their stores have changed are going to update. Go back to the image. That is Flux in a nutshell. Keep in mind that was a whirlwind tour of what we talked about over a half an hour last night, but that's where we are going to spend the rest of the night. We're going to spend the rest of the night looking at that flow, a less trivial example than that one, and talking through this in depth. If that just blew past you, don't freak out, that's like the rest of the night. 62 | 63 | How many people feel like they're with me? Phenomenal, it's going to be a great night. I didn't whether there was going to be two. I was afraid there was going to be two people and it was everyone. That's phenomenal. Obviously you guys are doing your homework. How many people got the homework? How many people reached React and Enlightenment? Nice, less but still a bunch. Okay, we're going to talk about Flux and Enlightenment tonight. {% include skipper.html time="00:20:00" %} 64 | 65 | Let's go thought that again on the image now. We start down here. This is where the button was clicked. When the button was clicked it fired an action. The action had data and a type. The store subscribed to that. The store updated itself and emitChange. When it emitChange the RecipesController was subscribing to the store and it then passed props down to children which updates the view. 66 | 67 | What this means is that the store has no idea how many components are listening to it out there. It doesn't know and the components don't know how many stores need the data that it's sending, right. They are blind to each other and that keeps the circle going, right. Send data out to the store and then we send data into the components. When we send data into the components, the data passes down the entire tree of functions, we rerender the entire app which just means that we are producing another version of the DOM. Which is diffed against the previous version of the DOM, and then we only paint and repaint the parts that actually changed and that's why it's fast. That's react in a nutshell. Once again, have a good night. 68 | 69 | ## React Router 70 | 71 | Okay, let's go back to where we left off {% include skipper.html time="00:21:14" %}. Let's take a moment to talk about the router, because it's super simple and we can knock that out. Let's take a look. We go down to the router. If you guys want to know where this is, you can Google react router {% include hublink.html link="https://github.com/rackt/react-router" text="React Router" %}. If you Google react router you will find github documentation {% include skipper.html time="00:21:41" %} with some very helpful overview documentation. I recommend you read it and we can use their docs as an example. Basically {% include skipper.html time="00:22:00" %} you're going to define, when you define routes, sorry let me go back to mine because this is really small. When you define routes, you're going to define a root route to handle path. Then you can define any amount of nested siblings and nested siblings and children. 72 | 73 | Here we have Recipes home, and we click back and forth between them. Notice the, let me zoom this in. Notice the change up here in the corner on the hash change. When we click Recipes home Recipes right, we change the route, we render a different component. That's really the only thing to know about react router. That's the critical thing to know. When we're rendering routes, sorry. When we are hitting different routes we are rendering different components. If you are nesting them arbitrarily deep, if you hit one that is rested arbitrarily deep it will render that one, not it's siblings, but all of its parents. Not the siblings of its parents and render what it's inside of, so you can create these nested representations of your app in the router. If you hit a nested route it will render all the components up the tree. 74 | 75 | It's basically a representation of that tree. Otherwise it would be difficult to do things like, to say that a nav bar, and you'll see this in the full recipes repo. It's typical to say that a nav bar is across every repo, right. Sorry, every component in your full repo. That would be a huge pain. If you have ten siblings and they all need some header, then you have to find that header in all of them. With this you don't, you just basically say that it is a parent or the parent has that and then it will be rendered automatically. I'll show you what that looks like. 76 | 77 | Actually I can right now. See how hello world on a RecipeBook {% include skipper.html time="00:24:00" %} is rendered and how the lengths themselves are rendered. They stay even though RecipeList and Home change. Let's look at how the code corresponds to that. Let's see. Notice that we have, let's see, RecipeBook, Home and RecipeForm, RecipeList, yeah, there we go. So when we click Recipes, that's right because it's default route. Okay, so can also, how's that happening? Because it's default route. You can define a default route, and default route will, if you have a bunch of siblings then without any further data, since we matched slash, this was the one that got returned. We see Home. Home is a component. Let's go up and look it what Home does. It just renders a string. Home render, there's some welcome text. There you go, there's your welcome text. 78 | 79 | If you click in Recipes it goes to slash Recipes. What is slash recipes? Well slash Recipes is RecipeList. Let's look at RecipeList, and they should look familiar we were just there. RecipeList is our map that's getting data. It's instantiating Recipe sub-components. Then it's putting that onto the screen, so we have different components mounting and unmounting here. Let's take a look at params. This is our last thing before we move on to the full repo, and I'll take a few questions before we do. 80 | 81 | Now we're going to add in params. You can see here var params = state.params. You do this in router.run and then you {% include skipper.html time="00:26:00" %} pass that into the handler in React.render. From then on you have access to the params in the tree of components, so as long as you are still passing it down in this.props.params. Let me show you up here what they looks like. See how it says RouteHandler this.props, that syntax will continue to pass your param data down. For instance if you have a splat or you have an id, you'll have access to that data down in your app. If you wanted to do something like this. If your route looked like, for instance, foo/:id and let's say it was `foo/:_id`, you would have access to something like ... Hold on let me zoom in further. You would have access to params, `params._id`, `this.props.params._id`. If you do this in your router then you have access to the id on your object down in the component. 82 | 83 | I'll show you that extensively throughout the full repo when we get into that. 84 | 85 | Let's see if I can log that out for you. This is going to be RecipeDetails, yeah there you go, okay. If you click on RecipeDetails ... you can see Recipes here and RecipeDetails. If I can see more then I can see the data for an individual item. I'm going to logout {% include skipper.html time="00:28:00" %} in RecipeDetails. Let's see, RecipeDetails in the render, render so let's see, console.log, params and this.props.params.id. Yeah, so there you go. You've got, and there it is, so it was YYY was the id as you can see back in the data. Which you can get use react Router. There you go, there's `_id`. 86 | 87 | You can use react router to get access to all of the information that is happening up in your params. Just needed to go over that with you first, because you are going to see that used extensively throughout the app. Whenever you see this.props.params know that they way that that is getting down to the component is that that starts up here in your router. It's going to be module, it's going to be in different files so it's going to be harder to find then. It's going to start in your router, then it's going to get passed down each subsequent component with a RouteHandler this.props. That's the story. 88 | 89 | With that I'm going to take a few questions and I'm going to switch over to the full repo and we are going to dig into it. Yep. 90 | 91 | **Speaker 3**: Is there some way to represent application state {% include skipper.html time="00:29:30" %} above and beyond object id, let's say you have a drop down open, do you have to use support query parameters or something else? {% include skipper.html time="00:29:40" %}. 92 | 93 | **Colin**: Yeah, that's a great question. I'm going to get you to the place that will answer that question very thoroughly. 94 | 95 | There's a list that shows you how to do this splat and they have documentation on how their splats work, and how, let's see. If anybody finds it do let me know. It's actually pretty important. Location, it wasn't there. Default route. Path batching guide, there it is, bam. Okay. There you go. If you go to docsguidespath-matching.markdown {% include hublink.html link="https://github.com/rackt/react-router/blob/master/docs/guides/path-matching.md" text="Path Matching in React Router" %}. You can all take a look at that. 96 | 97 | This will show you all of the things that you can do with react router and how you can match query params {% include skipper.html time="00:31:58" %} params. You will have access to that {% include skipper.html time="00:32:00" %} inside of, I think that would be inside of splat. This.props.params.splat. And you'd do star matching everything else. You can do literally whatever you want. You could put a novel in there and catch it. Others, yeah in the back. 98 | 99 | **Speaker 6**: What happened to 404 etc? 100 | 101 | **Colin**: Oh to 404? 102 | 103 | **Speaker 6**: Yeah. 104 | 105 | **Colin**: Yeah, great question. In ReactRouter there are a couple, let's see, let's just take the base case. You can transition, so you can do transition to. Let's see, yeah here you go. If you need to do something manual, like if you get something back from your server and it's something crazy and you need to redirect to some custom page based on that, you could even have anything call of function which navigates the user and send transition to this place. Basically it's arbitrarily flexible, right. But, what is nice is that there is also a not found route. A not found route is active when the beginning of the parent's path matches the URL, but none of its siblings do. Could be found in any level of your hierarchy allowing you to have context to where not found datas. 106 | 107 | **Speaker 6**: I went there and I didn't see a ... I was confused, but then I went there and I got blank as opposed to something useful. It told me it was a real 404 {% include skipper.html time="00:33:37" %}. 108 | 109 | **Colin**: Totally, totally. Yeah, anyways. Those are the two things that I'd tell you. You can do transition to if you need to do something custom you can do that from anywhere. You can add a not found route. Others, before we move on a go deep. 110 | 111 | **Speaker 7**: You mentioned you were {% include skipper.html time="00:34:00" %} gonna go over the homework. {% include skipper.html time="00:34:00" %} 112 | 113 | **Colin**: Yeah, thank you. Cool. Before I do that, because that will actually transition me right into, that was great T-ing up the whole rest of the night. Yes? 114 | 115 | **Speaker 8**: How do you to http? {% include skipper.html time="00:34:13" %} 116 | 117 | **Colin**: Good question. That's actually probably a good hour of the third we know. We're not doing any http tonight. There's no API. Long story short is that the pattern we are recommending that use, you have the store, [inaudible 00:34:45] the I module that uses super eight to talk to your server. That's the long and short of it. There's a lot there. We're going to be doing http and remote APIs next week. 118 | 119 | **Speaker 8**: During route transitions {% include skipper.html time="00:35:02" %}. 120 | 121 | **Colin**: During route transitions, yeah, yeah, yeah. That's a good question. {% include skipper.html time="00:35:06" %} I'm not sure. The route transition should happen instantaneously unless it needs to go for, you're talking about going for remote data, right? So I mean I think it's under that circumstance that you would flash it. But yes, you could flash something. 122 | 123 | [Edit: Use state loading true, and have ] 124 | 125 | **Colin**: Yeah, I will make sure we have an example of it. You're talking basically about a spinner example, right? I will make sure, I'm actually going to make a to-do right now. I will, let's see, thank you. I did, you knew that was important. {% include skipper.html time="00:36:00" %} I will have a working spinner example, a minimalist spinner example in one of the repos for next time. Thanks, that was a great idea. 126 | 127 | [Paula compliments **Colin** on having remembered to close Slack, as she accidentally messages him.] It was very responsible, wasn't it Paula. 128 | 129 | ## Homework: Controlled Inputs & Unidirectional Data Flow 130 | 131 | Let's talk about the homework. The homework was 006.3 flux enlightenment, and the homework was this, to fix the form functionality. Here we go, what's wrong? 132 | 133 | The idea is to rerender the input every time a new value comes in. The form broken was, I'm typing right now though you can't see that. You can't it because my hands are behind my laptop, and you also can't see it because there's nothing it going in. 134 | 135 | Can someone tell me why isn't there in this, it's a controlled input right, why if you have a controlled input, what's wrong? Why can't you see what I'm typing? Yeah, go for it. 136 | 137 | **Speaker 9**: When you type you stated that it deviates from the state of the applications, so it rerenders without the text. 138 | 139 | **Colin**: Yeah, absolutely. It rerenders without the text. You're setting the value of the form programmatically with value. If we look at the form broken here you will see value=, so value= empty string, and value always equals empty string because that's what the value equals. We didn't tell it anything else. We controlled the input. This is really nice. You don't have to do it this way, but if you do you can all {% include skipper.html time="00:38:00" %} sorts of really smart in-line form validation. It's nice to do it this way. It's also more of the pattern. Even what is being typed into your form inputs are going to be data that is reflected from the store. 140 | 141 | How many people in here know what an upsert is? Okay, all right so that's like maybe a third. Okay, so if I, and for those of you who know about an upsert, you know what I meant when I said implicitly creating a model when someone hits a form. We're going to have to probably go over that a little bit, because roughly two-thirds. Let's go over this. Let's say that a user comes to a, this is related to flux and enlightenment in the homework, form and let's say that the form is going to create a new instance, or if an instance already exists it's going to allow you to edit it. We'll uses Recipes because that's what you're about to see. If you don't have the id, if no id is returned we're just going to create an empty instance. If there is an id then it's going to populate the form fields with everything we already have. 142 | 143 | The way that that relates to upsert is that basically an upsert says, it's an update of an insert. If it doesn't exist put it there, if it does exist then make it. Then I'm sorry, then change it, right. An update insert. It's an update or an insert {% include skipper.html time="00:39:41" %}. Basically do the thing, make this data be there one way or the other. We are going to be following that upset pattern and I've found it very natural to do an upsert pattern with Flux because we're basically creating an empty record. The second someone hits it {% include skipper.html time="00:40:00" %} and your editing that record all the time. You're never putting data into a react component and then just grabbing it by id and serializing it and then putting it into a model. That's not really how the whole pattern encourage you to think. Though you can actually do that with reps, it's not great. 144 | 145 | What's better is to, the person types 'F' right. They type 'F' into or '7' or 'H' into a form field. You fire and action that says, hey input has got to be value. Then the store sees that. Says, okay the text value of my record is now this new thing that I got from the input. It emitsChange and the component, the entire app rerenders. Which is insane if you've ever done backbone, because you would never thing that way. That's the enlightenment. Why would you rerender you whole app because someone pressed 'F'. Obviously since we're diffing we're intelligently repainting, so it's okay. We're firing a whole tree of functions and that's not so bad. That's milliseconds {% include skipper.html time="00:41:28" %}, right. 146 | 147 | ## Recipes-Flux: Client Side CRUD 148 | 149 | Let's take a look at this. If you could all at this point go ahead and open up Recipes flux. Hopefully you've npm installed {% include skipper.html time="00:41:38" %} all and we're not going to hit a big bottleneck. I'm going to give you a little tour of Recipes flux first. There are some anti-patterns in here that we'll talk about. This repo is not canonical {% include skipper.html time="00:42:00" %}. There are some anti-patterns that I put in intentionally to look at. There's also some rough edges because I basically built it for this meet up. Let me spread this out a little bit. 150 | 151 | It's going to be your command. Your command is going to be gulp dev. That's going to initiate the web pack build. If you've used require than you've used AMD. If you've used browserify {% include skipper.html time="00:42:50" %} then you used common JS module. If you've used Node you've used common JS modules. Web pack is a bundler that has both. You can do AMD or common with the same bundler, which is nice. It has some other advantages too, like JSX hot loading. Let's see JSX hot loader. For those of you who don't know, who haven't seen this yet, you can do live editing in the brower. It's not a refresh, it's actually live editing the browser. Live editing without any refresh. That's super nice and if you use web pack you can do that. You should check out.{% include skipper.html time="00:44:00" %} 152 | 153 | This does not have that, but bonus points for a PR. If anyone PRs the hot loader to the Recipes, extra points. Everyone's got ... 154 | 155 | **Speaker 10**: gulp what? {% include skipper.html time="00:44:17" %} 156 | 157 | **Colin**: What's that? 158 | 159 | **Speaker 10**: {% include skipper.html time="00:44:18" %} what was the command? 160 | 161 | **Colin**: What was the command? 162 | 163 | **Speaker 11**: Gulp dev. 164 | 165 | **Colin**: Gulp dev. Sorry about that. $ gulp dev 166 | 167 | **Colin**: This is why I said this. This is going to be a little bit more free form than the last one was, because this into the app. It's a big interconnected machine, right. I'm just going to walk you through it. I could start in a lot of different places, so if you want feel free to ask questions, but I'm going to walk you through what it does. Hit localhost:3000, that will open it up. It's almost completely un-styled because we are going to talk about how to do styles in react next week. That's what's going to pretty much blow your minds about how ridiculously powerful everything just got. If you click on Recipes, let's click on stuffed chard leaves, you can see there's a ... We'll look at the UI and then we'll look at the data. Then we will look at the build and then we'll look at the app. 168 | 169 | Let's start with the UI. What does it do? This is still pretty raw. This is a servings thing here, but it does work. You can out in like '3' and it will change. These are actually converted to unicode. There's this NPM module called vulgarity, which you'll see. That actually take a fraction and returns the unicode symbol, so that this one-third is actually a unicode symbol. That's kind of fun. You can take a look at that code, but it will also do things like this. {% include skipper.html time="00:46:00" %} Hold on, there you go. If you put in an odd fraction it doesn't have a unicode for it will blow up. Anyway, rough edges. You can delete them, right. We've got CRUD on the recipes themselves. Then if you refresh, it'll just come back from the data. Then you can delete ingredients. That will be, like two kittens halved and peeled {% include skipper.html time="00:46:28" %}. Sorry okay, there you go. Then, let's see. 170 | 171 | That is reflected in the data. You'll notice one of the things that you'll notice is that as I type this, I just hit new Recipe. So I'm going to click new recipe. We'll look at some of the mysterious functionality. I'm going to click fooRecipe, then I got back to Recipes. I didn't save anything, but it's there. I click new Recipes and I say that, [inaudible 00:47:04] and then fooIngredient onelots first. Then we go to SeattleJS {% include skipper.html time="00:47:16" %} and we can see that it's there. We can edit it the same way. We can put like one thousand or one hundred or add another ingredient. 172 | 173 | Again, just giving you a tour of what we're going to be looking at under the hood. Then we can see that that updated, but there's no saving. The reason is that this is always a reflection of the store. Everything that you are seeing is data all the time. When you see it in a form field, when you see a new Recipe, this is already data. This is the upsert logic. A record didn't exist so we created one with default data. That's why if you click newRecipe a whole bunch of times it's just going to, if that case isn't handled intentionally so you can see {% include skipper.html time="00:48:00" %} as I keep going back and forth between newRecipes and data, you can see the upsert in action. It's just continually appending new records, right. It's creating a random GUID for each one. Now we're off the screen. 174 | 175 | A good next step for this repo might be to handle the case where someone doesn't edit the default data. So that you know that that person hasn't taken care of that, and you're not just creating lots of records. Okay, we looked at the UI and I told you we would look at the data behind this and then the build. Let's look at the data. If you go to Recipes flux, client, and then mock db, you will see that there are lots and lots of, actually there's only seven, Recipes. Basically it's a big JSON file. This will be moved over to the server next time, next meet up. We will do this over in API. For now, the store is requiring this directly. Each one has a GUI. That's important because we are going to be using that throughout the app to edit and to delete Recipes, it's title, portions, TimeInMinutes. Then this is also the important part to know about the structure of the data, is that we have an array of ingredients that ingredients itself is a collection. 176 | 177 | I was working with this example, but one of the things that really bothers me todo MVC is it doesn't have, it has collection rendering, but really where everything blows up ... You can do collection, you can do product collections in backbone and it doesn't really show how absolutely ugly it is, and I say that lovingly. It is a catastrophe once you're in to, the full catastrophe of backbone is apparent once you're into nested collection CRUD. The delete on nested collections is just really like this is all over the place. I think this example isn't nested collection {% include skipper.html time="00:50:00" %} CRUD and that's what you're seeing. We have collections of recipes and every recipe has a collection of ingredients and that's what we're going to be working with, both in form data, creation and deletion, all the rest of it. 178 | 179 | We could have modularized this more on the data side. We could have made a store for ingredients as well. I think there could be some really compelling reasons to do that. We didn't to keep it simple, it's just a single store. Let's go from the data now. We've looked at this, we see there's nested collections. We're going to go to the build and then we are going to start with the router. Okay, so the build what are we doing? Basically this is, have you've used gulp {% include skipper.html time="00:50:45" %} before? Oh man, it's so nice. All right. How many people in here have never used a build system before in job descript? Cool, okay welcome. 180 | 181 | This basically just starting a little server. The only really interesting thing in here that would be different from other projects that you may have seen is that if you haven't used webpack. This webpack.com thing and buildCfg and buildDevCfg are getting passed into a gulp task. You can see your required webpack here. This is not a gulp webpack task, it's just a different build system. You've got some JSON config in webpack, and your requiring webpack plugins and things like that. I'm not going to teach you webpack, except to say that you should use it. There's plenty of tutorials online for that. It's pretty defined problem, not so systemic. Then webpack is going to build using the Config. You can take a look at that later on your own if you want. Something to reference while you're doing your own build, just so you that basically a functionally {% include skipper.html time="00:52:00" %} gulp is consuming the config which is over here. That's the only thing I wanted to point out. 182 | 183 | Next, let's look at the router. That will give us the structure of our app. Now this router is a bit more complicated then our trivial example, but it's still not that bad. Once again, notice that you have a name, so like nameRecipes on line twenty here, there's no path. If there is no path specified, path takes name. That it will just inherent from that. Obviously that's not really feasible with line 21 because we need to take in some information from the params. Then the handler is the component that is going to be returned. Remember you're still requiring a tree of functions, and would be nested. Rather than nesting in the ... Sorry, give me ten seconds to think of a really elegant way to say this. Okay. 184 | 185 | Back in the very early example of reacts flux concepts, we saw nesting like this. We nested components inside of other components. That is still what you're going to do, unless {% include skipper.html time="00:54:00" %} one of these is a route. If it's a route, then you are not nesting it manually like this inside of another component. You're nesting it inside of the router here, and I'll give you a good example. Let's see, so recipe, oh. I drew a picture for this. Isn't it perfectly clear? Sorry. Let's take the simple ones first, because the simple ones are basically everything except the nesting crud. That's simple, right? This is also basically the routes, right? The top level ones here are the routes. The places that you can go to are home Recipes. Home just says, hey you're home. Let's look at these side by side. 186 | 187 | You go home. It says home rendered. You go to Recipes, you see a list of all the recipes. You go to new Recipes, that's slash create, and you go to this and it's Recipe, which shows you the detail for that individual Recipe. Let's look at this tree. This is a tree of all the components, and okay. Home, no children, Recipes, it's just a list. A list of the Recipes, no children, right? NotFound, obviously no children. RecipeDetails does have a child component, because when you're rendering a Recipe, unlike the RecipesList which doesn't render a component, you do have render the sub-component of the ingredient. Each Recipe in RecipeDetails has ingredients. That is also {% include skipper.html time="00:56:00" %} a pretty trivial view. 188 | 189 | Obviously the view that is not trivial is, and let me actually get all of this stuff out of the way. I made two of these. The view that is not trivial is RecipeForm. RecipeForm is actually, it's going to do a whole bunch of things. RecipeForm has the following functionality, and I'm going to when the, this is pretty much all still background to show you what is happening with flux. Within fifteen minutes or so we should have a full circuit of this app explained. Then you will be diving in and doing hands on stuff for the rest of the night. Sorry for the lengthy explanation, but there is a lot here. 190 | 191 | We have a button, and the button is added ingredient. The RecipeForm feature has basically three sections. One section is the addIngredient section. One is the, I'm going to show you this in the actual app, this is the RecipeForm. Let's look at the Recipe, so this is it. The first, oh you know what? Okay, I'm going to point at things. This, no other way. No, no, no. Okay. There we go. All right. That is one level, these guys are another level, and this is, these are all sections, right. These are all sections of this component. Only the RecipeForm as a whole gets a route. Like this button does not need its own route. It is nested inside of the component, {% include skipper.html time="00:58:00" %} just plainly nested. Does that make sense? Yeah, good. I got a lot of nods. 192 | 193 | We just nest this component inside of its parent, but if it gets a route then you don't want to just nest it inside another component because it's going to be matched. You put that in a router, and I'll walk you through this again at the end. 194 | 195 | Basically this is a section, Brown Rice and the other ingredient, the new empty ingredient. This is a section of the app. This is out ingredients collection. This up here, if you look at the data, that's that first section of the data. It's the title, the portions, the totalTimeInMinutes, and the instructions. Those just happen once. That's not a collection. That's just four things and they are actually explicit. They're still form field. That's it, that's our three sections. 196 | 197 | Let's go back to our drawing and look at those three sections. There's your button, right. Section one, let me actually bring that back. Add another ingredient is, there you go, button add and ingredient, that's the first section. The second section is IngredientFormInput which has four inputs, that's this top part here. Then the last section here is, and by the way this is actually a representation of the files. This is what files, and I'm going to out this, I'm going to don this right now. Sorry, I should have done this at the beginning. I'm going to share this through gitter, so incoming. Let's see, and {% include skipper.html time="01:00:00" %} okay there you go. Now you can click into those if you'd like to. Nice, okay. 198 | 199 | The one that has all of them, this is actually a representation of the app. Has anyone used lab view? 200 | 201 | Students: Yes {% include skipper.html time="01:00:25" %} 202 | 203 | ## Someone Please Build This 204 | 205 | **Colin**: yeah, yeah. I was actually thinking because we have the guarantee now that you are working with a tree, you can basically make visual app editor. We're almost to the dream of the '90s. We're closer now than ever really. Anyway, it's my dream still. I won't impose it on you. Visual app editor, okay, I'm going to. The first step would be just print it out, right. Like log out, visualize the whole app. Someone has got to do this. Visualize the app, and just spit out the tree of components and all of the children, and the next is a delete or a read. It's just interacts with your files - would be trivial in Node. This just totally has to exist. We're pretty much there now. Okay. 206 | 207 | ## Nested Collection View Rendering 208 | 209 | Anyway, this our IngredientForm, each button, each on of the has to be deleted. Each row, like each total ingredient can be deleted, but then each ingredient also have four inputs for the four different aspects of the data. Now, if this were prettier it would be easier to see, but they are all switched together because there is no CSS. Each on of these has an input, which is it's own component. 210 | 211 | Let's take a look at how we're moving data through the system. Now this is where is starts to get complicated. That was the simple part, right? Okay. All right. Let me close these out. {% include skipper.html time="01:02:00" %} all right. Okay. Let's start with the smallest input we can. Let's start at the bottom. Let's look at input. What does input do? Input is blind. It is totally blind, and you can tell that it's totally blind because it takes ... Everything you are seeing in this input is props, right. The input takes in a callback and the input takes in lots of props from the component. Someone else is managing state for this component. If it weren't then it wouldn't be reusable, so that is why we can use the input component here and here and the input component here. 212 | 213 | We have completely different values that are getting passed in, but the component still functions the same way. It takes in a callback and handleInputChange fires a function that its parent passes into it, and it takes in a id that its parent send to it, and it takes in a value that its parent send to it. 214 | 215 | This is really the homework. What you weren't seeing in the homework was this flow where the value is this.props.value. The input value is being passed down to it from the parent and it's firing an inputCallback. Let's look at something that is actually firing this, so ingredientForm is firing this. Let's see, if we instantiated an input, of it's IngredientFormInput, okay. There you go. {% include skipper.html time="01:04:00" %} 216 | 217 | When we instantiate the input, we're passing props to it and we're this.handleChange. Well what is the inputCallback that we are looking at, this callback that the input is going to fire when it changes. Because remember anytime the input changes, this is like someone types a single character into it, it fires this. On change the input is going to what? The input is going to fire this.handleChange. What's that do? It fires an action, and there we are. That's the pattern in effect. When the input changes at anytime it fires an action that says, hey here's your data, hey here's your data, hey here's your data, hey here's your data, and the store or any number or store update themselves. Once they do they say emitChange, they get new props and then how does the input know what the value is? Because it's setting the value of itself to this.props.value. Where's that coming from? Well, let's look. 218 | 219 | Value is value, which is coming from props, and if we go up the chain once more we can see, let's see, IngredientRecipeForm. If we go up the chain enough we get to out upsert. So go ahead and go to RecipeForm. Basically if you look at getInitialState, we're setting states either if we have an id, we set it to the id. If we don't have an id, then we implicitly create a new Recipe and that right there, that's the upsert. That allows us to be editing a new entry even if {% include skipper.html time="01:06:00" %} the person just clicked new, we're still editing data. Here's what we're not doing, we're not waiting for the user to put a bunch of stuff into a form, and then grab the form elements by id with jQuery, put it into an object and then send that somewhere. Which is what's normal. We're holding that intermediary state. I'm going to pause here and take maybe three or four questions, and then we'll continue. We'll go forth. 220 | 221 | **Speaker 13**: You choose to, every time the input is made through it should have a function that goes to the parent. The parent- 222 | 223 | **Colin**: It doesn't go to its parent. It was passed down from the parent in props and it fires that function, yeah. 224 | 225 | **Speaker 13**: Right, so the [inaudible 01:06:40]. 226 | 227 | **Colin**: Yeah. 228 | 229 | **Speaker 13**: Why choose this over [inaudible 01:06:45]. 230 | 231 | **Colin**: Good question, because the input would be less reusable. You're passing data down to the input. If you want to reuse that input everywhere, then the more that it's blind the implementation details of its parent, the more reusable that component is. Componentization {% include skipper.html time="01:07:08" %} in a word. Yeah, in the back. 232 | 233 | **Speaker 14**: Oh yeah, right {% include skipper.html time="01:07:15" %} how do you know to use this.props.value {% include skipper.html time="01:07:16" %} 234 | 235 | **Colin**: What are we using? This.props.state, is there state in there {% include skipper.html time="01:07:22" %} 236 | 237 | **Speaker 14**: [inaudible 01:07:23] 238 | 239 | **Colin**: Oh yeah, right. Okay. Great question. 240 | 241 | **Speaker 15**: Can you repeat the question? 242 | 243 | **Colin**: Yeah, I will repeat the question. Absolutely. So the question is, why on earth when you do handleInputChange, so this.props.value is what came in for the parent, but this is the function that is firing the action. The parent is going to determine which actions. Actually the parent determines what happens in that function because the parent is passing that function down, right. handleInput is {% include skipper.html time="01:08:00" %} getting passed down to it. What this is actually doing is it's, you can see ref = inputValue, that's right here. A ref works like this. A ref is like, well literally here's what it's doing, it's getting a reference to the actual dom node. That's what's actually touching the DOM and getting the data out. Where you can't do that because you're ... what's that? 244 | 245 | **Speaker 15**: I think it requires {% include skipper.html time="01:08:30" %} value {% include skipper.html time="01:08:31" %}. 246 | 247 | **Colin**: Return, hold on. We're up here, there's two places. Value down here is what's getting rendered. That's what's going to be on screen, but up here in handleInputChange it's this.refs.inputvalue.getDOMNode.value because you are pulling the letters out of the DOM out of the actual input. Does that make sense or does that not answer your question? 248 | 249 | **Speaker 15**: It makes sense, but when I was typing it worked {% include skipper.html time="01:09:02" %}. 250 | 251 | **Colin**: What worked? 252 | 253 | **Speaker 15**: Instead of saying this.refs, I said this.props {% include skipper.html time="01:09:09" %} 254 | 255 | **Colin**: Oh, it worked. Hold on. 256 | **Colin**: Did you save? 257 | **Colin**: I would bet you that wouldn't work. 258 | 259 | **Speaker 15**: Maybe I didn't {% include skipper.html time="01:09:19" %} 260 | 261 | **Colin**: That's okay. I think we probably learned something. All right, others? Yeah. 262 | 263 | **Speaker 16**: So, [inaudible 01:09:30] if you're passing [inaudible 01:09:32] 264 | 265 | **Colin**: Yep. 266 | 267 | **Speaker 16**: [inaudible 01:09:38] 268 | 269 | **Colin**: ingredientFormInput? 270 | 271 | **Speaker 16**: [inaudible 01:09:42] 272 | 273 | **Colin**: Yep. 274 | 275 | **Speaker 16**: [inaudible 01:09:44] change over there. You're completing the value again [inaudible 01:09:50] 276 | 277 | **Colin**: Yep. 278 | 279 | **Speaker 16**: [inaudible 01:09:52] 280 | 281 | **Colin**: Yep, so handle change actually take arg {% include skipper.html time="01:10:00" %} 282 | 283 | **Speaker 17**: I have a question about the [inaudible 01:10:18] thing. 284 | 285 | **Colin**: Yep. 286 | 287 | **Speaker 17**: Is it preferable to go and get the DOM node {% include skipper.html time="01:10:24" %} taking [inaudible 01:10:26] object that comes in. Just grabbing the. 288 | 289 | **Colin**: Say that one more time. 290 | 291 | **Speaker 17**: The manual input [inaudible 01:10:32]. 292 | 293 | **Colin**: Let's get there first. Yep. So here? 294 | 295 | **Speaker 17**: [inaudible 01:10:36] input change. [inaudible 01:10:38] the actual [inaudible 01:10:39] we pass [inaudible 01:10:42]. 296 | 297 | **Colin**: Like on change? 298 | 299 | **Speaker 17**: Yeah. 300 | 301 | **Colin**: Okay. 302 | 303 | **Speaker 17**: Those actually have the text on it. Is it preferable to go back to DOM and get Node for some reason or [inaudible 01:10:53]. 304 | 305 | **Colin**: That would be even more generalizeable , wouldn't it? I think. 306 | 307 | **Speaker 17**: [inaudible 01:11:01] 308 | 309 | **Colin**: Yeah, yeah. Yep. If you can do that I think it would be more generalizeable. I'm not sure that would work in very circumstance, but if you could yep. 310 | 311 | **Speaker 17**: It worked in this one [inaudible 01:11:10]. 312 | 313 | **Colin**: Cool. Others? Yep. 314 | 315 | **Speaker 18**: Do you prefer to pass these callbacks as props or have you ever used the context to delegate a callback? 316 | 317 | **Colin**: Props. 318 | 319 | **Speaker 18**: Props. 320 | 321 | **Colin**: Mm-hmm (affirmative). Yep. Others? Yeah. 322 | 323 | **Speaker 19**: is it possible to pass custom props to RecipeForm 324 | 325 | **Colin**: That's a great question. We're in righter right? 326 | 327 | **Speaker 19**: Yeah. 328 | 329 | **Colin**: On we're on line maybe nineteen. 330 | 331 | **Speaker 19**: Yeah. 332 | 333 | **Colin**: Let's actually take move that isn't default route. Let's take a complicated one. Let's take this one. 334 | 335 | **Speaker 19**: Yeah. 336 | 337 | **Colin**: Is it possible to pass custom, what was this? 338 | 339 | **Speaker 19**: Custom props. {% include skipper.html time="01:12:00" %} 340 | 341 | **Colin**: Custom props. 342 | 343 | **Speaker 19**: [inaudible 01:12:03] 344 | 345 | **Colin**: Yeah, it is. Yes, yes it is. That's a great question. It's a really really good question and I'm going to show you how to do it right now. The question is, can you pass custom props to the RecipeFormHandler, what's going on there. Here's how you do it. App, check this out, so RouteHandler. This is above it's child, right. If you want something custom you can do foo="bar" right there. That will be available in the children. You can pass it ... 346 | 347 | **Speaker 19**: [inaudible 01:12:43] 348 | 349 | **Colin**: It'll be available as this.props.params.foo or this.props.foo. You'll have to check me. It's going to get fired down the chain. 350 | 351 | **Speaker 19**: [inaudible 01:12:55] 352 | 353 | **Colin**: I don't know about that. I don't think so. If you're all siblings then you have the same parent RouteHandler and I don't know that you do it for just one of them. But you can get it to all of them. David, are you in the room? Yeah. Do you know about that? Is there a way to use RouteHandler to target one of the siblings rather than all of them? If you want to pass custom props down, I don't think you can. 354 | 355 | **Speaker 20**: What do you think its target was [inaudible 01:13:31]. 356 | 357 | **Colin**: What's that? 358 | 359 | **Speaker 20**: [inaudible 01:13:34] 360 | 361 | David: What do you mean by target [inaudible 01:13:37] 362 | 363 | **Colin**: Yeah, run up for a second if you want. Hey Dan, can you hand David a mic? 364 | 365 | **Speaker 20**: I was just ... 366 | 367 | **Colin**: Just yell, just yell. 368 | 369 | **Speaker 20**: [inaudible 01:13:48] 370 | 371 | **Colin**: No worries, no worries. So, they question was basically, if you're in- 372 | 373 | David: [inaudible 01:13:52] I can show you here. 374 | 375 | **Colin**: That's right, that's right. I'm just like you need to come over here and look at my computer... David... Why are you staying over there? {% include skipper.html time="01:14:00" %} The router, let's say that you wanted to pass custom props, right, to one of the siblings, right. You would do that this.props. You could do that here. 376 | 377 | David: It would be available in its children. 378 | 379 | **Colin**: Would it- 380 | 381 | David: You could just keep doing that, because let's say RecipeDetails is going to have, let's say RecipeForm, is maybe going to have a RouteHandler and just keep passing it down. 382 | 383 | **Colin**: You're going to keep passing it down, keep passing it down, but what about if you only wanted to target one of the children of all the siblings, I don't think you can do that, can you? Like only one of these siblings out of the box, I don't think you can. 384 | 385 | David: From the parent? 386 | 387 | **Colin**: Yeah, nope. You're just going to pass it down to all the children, right? 388 | 389 | David: [inaudible 01:14:55] 390 | 391 | **Colin**: Because it's actually a more general question. Like if you wanted to pass props down just to child components. 392 | 393 | David: You can do it if you are instantiating each one of them in the component manually. Then you have control over the instantiation button. 394 | 395 | **Speaker 20**: [inaudible 01:15:12]. 396 | 397 | **Colin**: Right. Yeah exactly. But you are abstracting that with react router. Have you used react router yet? You haven't yet, okay. [inaudible 01:15:23] it's all good. 398 | 399 | **Speaker 20**: [inaudible 01:15:25] 400 | 401 | **Colin**: Okay. 402 | 403 | **Speaker 20**: [inaudible 01:15:30] 404 | 405 | **Colin**: No. 406 | 407 | **Speaker 22**: So, [inaudible 01:15:43] 408 | 409 | **Colin**: Correct. 410 | 411 | **Speaker 22**: [inaudible 01:15:55] 412 | 413 | **Colin**: It's firing action that- 414 | 415 | **Speaker 22**: [inaudible 01:15:57] {% include skipper.html time="01:16:00" %} 416 | 417 | **Colin**: Yeah, he was asking that. I'm not sure why that works. It's a great question I'm sure that I wrote that at 3 AM and it worked. I actually have to look at that again now. Yeah, I'm pretty sure he's right that I shouldn't be doing that, and I need to look at that again. Yes, thank you. I think actually what you should do if you are interested in that is you should console.log inside of that function and see what this.props is and see if this.props is the child element is the input or if it's the parent. 418 | 419 | **Speaker 22**: [inaudible 01:16:35] 420 | 421 | **Colin**: What is this? It might be the this of the child, and if that's true, that's why I did that a 3 AM. If it's not then I have no idea why it works. Are there others before we keep going? These are great questions, really really great questions. Yeah. 422 | 423 | **Speaker 23**: Is there any danger to always passing dot dot dot this props 424 | 425 | **Colin**: Yeah, you could do that all the time couldn't you? Then everyone would know about everything. I guess, no. If you want to be able to console.log out this.props and know what that component should have I think it would be more useful certainly to have intentionally only pass down really what the component needs. Yeah, you could use the [inaudible 01:17:24] spread to paint it on every component everywhere. 426 | 427 | **Speaker 23**: [inaudible 01:17:28] 428 | 429 | **Colin**: Yeah. 430 | 431 | **Speaker 23**: [inaudible 01:17:30] 432 | 433 | **Colin**: I just imagine for a huge app, that would be a lot of ... You would be passing a lot of JSON {% include skipper.html time="01:17:37" %} down. Anyway, for what it's worth. Okay, so we're almost to the point ... We're pretty much to the point now where we're going to look at where that action went. Then I'm going to turn it back to you guys and we're going to spend the last forty or thirty-five minutes implementing a little feature. One more question before moving on. Did I see a hand? {% include skipper.html time="01:18:00" %} 434 | 435 | Let's look at where this actions goes. We fire an action from RecipeActions and RecipeActions is going to trigger an inputChange. Let's go look at RecipeActions. There are a whole bunch of different actions here. One of them is INPUT_CHANGED, and INPUT_CHANGED is just going to, once again, take whatever data we out in which is that object. It's going to pass it through, and with this type. Now let's look at store, and the store is listening for that type. It's like, okay there's an INPUT_CHANGED. So what's it going to do? It's going to say, okay RecipeStore which is basically like this, .updateRecipe and it's going to pass in the data. Once again, proxying that through. 436 | 437 | It's unpacking it, right. It's saying `payload.data._id`. It's basically proxying it through and unpacking it a little bit. Once updateRecipe changes, then RecipeStore.emitChange is going to fire and that's going to let the components know, hey there's new data. They're like, oh I get new props and then they rerender. That's the loop. Once again, that's the whole loop. Something changed in the user input, a change was fired with data, the data went into the store, the store said, oh I know that action type here is the function that I fire when inputChange I call updateRecipe when the input changes. This should really be less general than inputChange, it should be RecipeInputChange. No, actually seriously that should be more general. INPUT_CHANGED is not general enough. It should be RecipeInputChange, but it's not. 438 | 439 | UpdateRecipe fires and that goes up here, which is updatRecipe. Let's see, here you go. Which {% include skipper.html time="01:20:00" %} goes and gets a Recipe, and it either digs into the object and changes the value. Once it's done, it says, okay I'm done. At that point, it does emitChange. EmitChange is, if you go back to the picture, that last loop here. Then the components subscribe. Typically ControllerViews subscribe, right. Most of the components in your app are not going to be subscribing to the store. They're just going to take props in whenever they get it. How the components know to update is they are subscribing to the store. We're using McFly because Mcfly makes that easy. It just automatically handles those subscriptions under the hood. Yeah, go ahead. 440 | 441 | **Speaker:** So is relay going to be part of how that data bubbles from the ViewController down and back? 442 | 443 | **Colin**: That's a great question. I have a guess, and I wasn't ... Who saw Relay? Who knows Relay from the React conference? Okay, so let me recap the question. So relay is how Facebook is using react. It basically replaces Flux, and it think it is very likely that Mere Mortals {% include skipper.html time="01:21:38" %} will not be using it anytime soon at all. That was my initial assumption from seeing it and that is definitely, definitely further confirmed by talking to Facebook people. It does seem like they want it to be really general and they are going to release it. It's basically ORMish {% include skipper.html time="01:21:55" %}. Basically here's what the component does, the component has a shape of JSON, it's got. {% include skipper.html time="01:22:00" %} It's like, here's what I need, the component then sens that shape to the server. The server receives that and is like, okay, and constructs that behind the scenes and then gives it back. 444 | 445 | That's really sweet, but if you have tons of really smart backend people to make that fast, but that's really ORMish and making that work for any data shape is not quick. I don't think that's a simple problem for lots of people. That was initial assumption watching [inaudible 01:22:23] and I think that and talking to Facebookers, I think even more so. I don't have high hopes for Relay in the short-term. I think people should dig into flux and just get in the pattern and probably roll their own flux implementation. I think right now pretty much everybody should be rolling their own flux implementation working from dispatcher, because there's nothing to it. You can do it in a couple hours and then you really understand it from the bottom up. Everyone is going to have different opinions. There's a fly, also. 446 | 447 | I encourage people especially, Facebook hasn't really said anything yet, so I don't know. I anticipate that it's going to be a really long time before they release it. I might be wrong, but I think there's some big problems. Anyway. Really quick, yes. 448 | 449 | **Speaker 8**: [inaudible 01:23:08] 450 | 451 | **Colin**: Next week, yes. This week is just one score. At this point, what I'm going to do is I'm going to take questions about the data flow, and I'm going to let you guys move on to your assignment. What I'd like you to do is pair up in two's. Pretty much no more than two. If you have to do three, do three, but two is really good. What I'd like you to do is, I would like you to implement a new feature that completes the loop. These is the suggested feature, you may deviate with your partner if you would like. Here is the one that I think would be good, new route, new view and it's a series of dates, each date can have a Recipe. So you're planning a week. You have, maybe it's like seven days you can structure. It could be like, let's say {% include skipper.html time="01:24:00" %} seven days, each day can have a Recipe. You can select a Recipe for a day, and then they are all aware of each other so that when you select one Recipe from a list then it's not available for the others, something like that. 452 | 453 | If you're good with this, which I think a lot of you are already, that's probably like a half an hour thing. If you have no idea how to get started with that, then I'm just going to keep talking through it and ask me questions. How many people feel like they're most of the way there to feeling like they could probably do that? Yeah. We've got like over half the room, so that's good. 454 | 455 | Let's walk though real quick in the abstract how you approach this problem. The first thing I would do is create a new component, and I would call it Date Picker. Let's say we have new component which is called Date Picker, and we're going to subscribe that to a store. You can create a new store, and the store would be a date store. There's some flexibility in how you structure that. You'll have a new actions and it will be date actions, and it will be like date selected or date removed or things like that. You can do the same kind of crud on a date that you can do on a Recipe. You can add a Recipe to a date. There are exactly seven recipes in there. You can plan a week of meals. 456 | 457 | What you will want to do in the router is just add a new route that's like slash date picker. Then just display that and you can use, it has bootstrap already, so you can just add bootstrap drop down if you want. Or just make it enter work from an id or something right now. {% include skipper.html time="01:26:00" %} That's if you're feeling really good and you feel like you could just dive in right now, then feel free to grab a partner and try it, go for it. If you're not then let's just start asking questions. I will be up here talking, so this is now permission to ignore me if you want to with your partner. If you want to keep listening, what I'm going to do next, and that's optional right now. 458 | 459 | You can either just go into your own world and try to make that happen, or you can keep listening to me and I'm going to walk you through how I implemented all of the ingredientForm stuff. We'll take a two minute break for you guys to get started and then we'll dive back in. Thanks for listening. Great first hour and a half, that was intense. Yes. 460 | 461 | **Colin**: Check, check, check, check, okay I'm back on. Okay that was a few minutes. Everyone got pizza and got [inaudible 01:27:04] again. How many people are diving into the feature? How many people are diving into work? Good. Okay that's a really good ... How many people are interested in my just continuing talking about this? Okay, cool. That's pretty small. All right. I'm going to talk through some more of the example, and we'll just keep looking at it. 462 | 463 | For those of you who are watching and ... Angela, you look really happy. Did you get something? 464 | 465 | **Angela**: Duke just won. Sorry. 466 | 467 | **Colin**: It's all good. 468 | 469 | Let's talk about {% include skipper.html time="01:28:00" %} the flow again for those of you who are listening. We're going to start with RecipeForm, and RecipeForm is this, let's look here, complex feature that you can add an ingredients and each ingredient can be deleted. The ingredientForm actually has four sub-inputs for the ingredient itself, the amount, the quantity, like teaspoon, and the modifier, like chopped or fresh, things like that. The ingredientForm input is actually called directly as well because there are four singleton pieces of data in the [inaudible 01:28:58] we're working with. Here we go, title, portions, and totalTimeInMinutes is going to happen from the top leverl view. That's like you can see here it is, RecipeForm. Yeah, here you go. 470 | 471 | Each one of these ingredientForm inputs is instantiated directly with the label placeholder, value and it's, let's see. These are instantiated directly, whereas the ingredientFormNodes, let's look at how those are instantiated. ingredientFormNodes is a map over this.state.ingredients, so we grab the ingredients from either from the recipeStore. {% include skipper.html time="01:30:00" %} This is a public getter on the RecipeStore, right here, we're going to go out and get it and then bring it back. Let's see, RecipeStore.getRecipes just returns this.Recipes. That's the public getter is just give me everything in the underscore Recipes and the private store variable. There were a bunch of people over here who raised a hand, so can I take questions from people over her just generally on stuff tonight, anything. Yeah. 472 | 473 | **Speaker 26**: What about the style stuff [inaudible 01:30:50] 474 | 475 | **Colin**: The style stuff is next time. That's related to ... I'll give you guys a brief preview of what will be mind blowing next week. It's this. I'll say nothing more. I'll tell you I don't think anyone is every going back, but it's pretty big. Style is next week and it's going to be pretty good. Other over here? Okay, so we're going to give everybody about ten fifteen minutes here and ten to fifteen minutes of digging in. Then we'll come back together and I'll answer a whole other round of questions of challenges you ran into. Go ahead and we'll get back together around 8:50. {% include skipper.html time="01:32:00" %} 476 | 477 | Okay, so as you may have guess this is homework. I'm happy to go over this with you guys at [inaudible 01:32:10] office next Tuesday if you are in the middle of working on it. This is a great goal to accomplish for the next one. I will say, this is the goal. When you come in next time, you will have created a new store, new actions for that store and new components with child components that take data from the store. You can follow the pattern of the other inputs. That's got to be the next way point. You can do that with date or if you get clever and have an idea you can do that with something else. We'll go over a couple of them next time. Next time we are going to be fast and furious. We're going to be doing API. 478 | 479 | I showed you just briefly, but this like ... Be here next time it's going to be super fun. It's going to be super fun. The last thing that we'll do, and it will just take, I'll take any questions that you have about the homework, about this uni-directional data flow. Which is different from backbone. Ways in which you are approaching the homework and try to answer those before we take off. Anyone want to start? Or you're all good. Yeah, go ahead. 480 | 481 | **Colin**: Oh. Yes. I'm going to point you to a few places. Look at this guy, React Tween State, and look at him on twitter. Everything he is doing, at Facebook. He's really really bright. Cheng Lou I think his name is. He's building tween state. This is still pretty early. There's also a reacts animations, a data visualization. Oh, Awesome React, this is a great community resource in general. Animation, yeah there's not ... You can see React Animation, State Animation, React Animation, so there's not, there's only three in all of Awesome React. There's only three [inaudible 01:35:40] for animation. It's really early days. Of course you can still use CSS, just put it in a style sheet like you would normally. Put a class on an element. Anything else that, go ahead. 482 | 483 | **Speaker 30**: Isomorphic? {% include skipper.html time="01:35:54" %}. 484 | 485 | **Colin**: Yes, we are going to talk about isomorphic {% include skipper.html time="01:35:53" %} next time. We're going to talk about API and isomorphic. [Edit: We ...didn't end up talking about it. There are too many unsolved problems, just like with flow. They need more time in the oven.] {% include skipper.html time="01:36:00" %} that's actually pretty much it. It's going to be CSS, how to do styles and APIs. That's going to be a lot. In the contents of talking about API we're going to be talking about isomorphic. Thank you everyone. We'll call it a night. Thanks so much for your attention. Everybody was great. Thank you. 486 | -------------------------------------------------------------------------------- /part-3-styles-with-radium.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: tutorial 3 | title: "Learn React & Flux: Part III" 4 | description: Brought to you by Formidable Labs and SeattleJS, Colin walks us through Facebook's React framework in part three of this three-part series. 5 | video: 6fhTawDEE9k 6 | repo: https://github.com/formidablelabs/recipes-flux 7 | --- 8 | 9 | ## Welcome 10 | 11 | **Colin**: Tonight is really some edge features and actually it's different in the edge features weren't even ready so we can use other edge features. We were going to do Flow and we're going to do Type-Checking and Facebook told us that it's really not even ready to be taught yet. It's changing and it's not done. There are two things that everyone has asked about and we knew we were going to do them anyway and decided to focus the whole night on them. Those two things are: how do I talk to a server in React like a small thing, right? How do I style all of my applications in React? Also a small thing. 12 | 13 | Really, there's no solution for either of those things so we're going to solve those problems tonight and first thing we're going to do ... could you put my computer on? Thank you. 14 | 15 | ## Review 16 | 17 | The first we're going to do and we did this last time too and everybody was really into it, I'm going to pump up the font size. This is advanced night for the three advanced workshops so we're going to do some crazy things tonight but we're going to go back to basics and I'm going to do a review as well. We're going to get our head back in context so here's a run through of React from first principle just in case you have been following along but it's been a few weeks since the beginning. 18 | 19 | We started out last time with a blank file and we know we need React and we know we need to transform JSX which is this markup language that transforms this kind of xml syntax, html-like syntax into a tree of functions. Again, conceptually we know that that tree of functions is going to become a virtual DOM and that virtual DOM is going to get giffed against the previous version of the virtual DOM and the changes only are going to get applied that's why we perform it to re-render the application all of the time based on whatever data is in the app. The concept in React is that your data ... your user interface {% include skipper.html time="00:02:00" %} is always current up to date with your data. 20 | 21 | Let's look at the whole world. We need a container because we've got to attach the app somewhere so we add a container and then we have our JSX. Our JSX is just returning a div and here's something we talked about before: remember that if you're returning a div, you can only return one root element. You can't have any siblings to that div. You can have as much as you want in here, but it's got to wrapped in one element. 22 | 23 | Then we call React.render and we give it a component. This component can have others in it. 24 | 25 | When we broke recipe list into recipe form, and then we instantiate those two components inside of the parent component. We say in React speak that the recipe book owns recipe list and recipe form. What that really means, is that recipe book is in charge of the state of recipe list and recipe form. It can pass props down so let's talk about props. What are those? 26 | 27 | We can pass data, let's take some data here. We have a syntax in JSX that looks like attributes. It's not attributes, it's actually more like arguments to a function. Add title, Stuffed Chard and instructions to Stuffed Chard. Those are going to be accepted by recipe which is up here and you can see once it's instantiated the title and instructions are going to be available at this.props.title and this.props.instructions. Those are going to get the data is going to be passed down. {% include skipper.html time="00:04:00" %} 28 | 29 | We can do basically the same thing starting at the top level. Start at recipe book, in React.render, and pass in some data we know that recipe data is an arrayed data, we pass in that array, it gets piped down all the way down through to the recipe that needs it and just in the same way, you can then call this.props. In this case, we're going to do a map and map over that array and we're going to return it for each item in the array passing through the data into props. 30 | 31 | Then we talked a little bit about state and use that initial state to preserve state of components. Remember, when we're re-rendering the application every time there's new data and the problem with that of course is that let's say you have a sortable list, okay well if you sort the list down to two elements, and then you re-render from the existing data from the store, well of course you just used the order of your list and you're back to reflection of the store. You always want it to be a reflection of the store, you want it to be a reflection of the store where the data is for the app accept when you have a local state in a component like a sorted list. 32 | 33 | The state here could have items to show where all items showing equals true, all items showing is false then you go do another check for your item it should show the way you manipulate state is never correctly. You never do this.state.fu=, you only use this.setstate. this.set state will force a render and when you force a re-render it will go make whatever calls it wants to the store to get the data. 34 | 35 | We then got into Flux and Flux I want to go grab {% include skipper.html time="00:06:00" %} hold on just a second ... Can you screen back up for just a second? I just need to go run around and find something really quick. Okay; back up thank you. 36 | 37 | Remember that we talked about Flux and in Flux we have components which are from React, we have actions, we have a store, and we are connecting those things together with a library. If you're using Mcfly, actually we've already swapped out Mcfly for something else called Nif so we're going to be working on that tonight and then I had already had a replacement for Reflux so that's like six weeks and three libraries so we're averaging one rewrite every two weeks right now. That's about where the ecosystem is. When you ... how is that in the back? Is that okay? Higher, bigger? That okay? All right. 38 | 39 | The recipe component, let's say that a button's clicked. The button's clicked, it fires action and the action may or may not call an API and that's what we're going to talk about tonight actually. The first item of tonight is how do I talk to a server? The second item tonight is how do I style my app? The actions the store subscribes the actions. Actually, all stores receive all actions and that's what you see; you're firing off the actions with data, all of the stores should do a check and say, "hey, do I need this?" The store when it goes ahead and updates itself, does whatever mutation it needs to do, then fires emit change {% include skipper.html time="00:08:00" %} the recipe's controller or the app itself all of the components will subscribe to changes in the store, that will re-render the application, pass the props down to recipes and voila; your app's current. It's uni-directional; you're not doing this.model.set or this.model.get. You kind of are doing this.model.get. You can do a public getter. 40 | 41 | ## Flux & REST Apis 42 | 43 | That's actually what we're going to start talking about tonight. I haven't done any decks the entire time but inevitably this is going to be really helpful. How do you do async in Reactive Flux? The primary question which is being debated online and hopefully this video when we put it out gives something for people to reference. Both of ... Teaching the first one was basically building an app based on what was publically known and available. The second one last week was pretty easy, too because it was a much more complex app but it was all client side which people basically knew. We knew how to handle that. But this lecture installation was really hard because both of these two things are not solving the community at all. Facebook has their own solutions and we're having to ... if we're using Node or using Python the backup that Facebook is using, having to reinvent these patterns ourselves. Hopefully this serves as a reference point for all of you and it's been a bit of a research project on our end. Research project, lib building, talking, bouncing ideas off. 44 | 45 | I don't want you to take this as the final word, this is like, "how do you answer these major questions? How did we answer these major questions? In our own apps like this week and probably in two weeks maybe this is already irrelevant". But hopefully we have longer than that but we had to get on the train at some point so where should async live? {% include skipper.html time="00:10:00" %} The problem with calling the API from the store is that what comes back from the API may need to be digested by more than one store. Also, they're synchronous and they'll blow right by a promise and also the crossstore dependencies issue can get really hairy. Stores leave stores, leave stores; you could end up with a graph back there and you don't really want it. You also don't want them to be components because in general components should be on the receiving end of data. The problem with actions is much, much less so what we're advocating and I'm going to give you a high-level overview of this and then we're going to dig into code that contains this pattern. 46 | 47 | What people are doing with Facebook and what we've found is working in our apps we're building, we have four or five React apps that are already going so with the patterns that we're using, that we're doing is async in the actions and we think it belongs to the action creators for the following reasons. I'm going to walk you guy through this series of diagrams. Again, and then we'll get to code. 48 | 49 | This is a read so the component uses a public getter ... this is what we did last week for anybody who wasn't here last time. The component uses a public getter and it goes up and it talks to the store. The store then returns the data and then we can see our little happy, yay I got that arrow and it gives the data back to the store. No http; just local. That's what we did last week; we were using public getters from the store. 50 | 51 | If you want to do an async read it's pretty much the same thing. Your component calls the public getter on the store, the store says, "hey do I have anyone with this ID?" {% include skipper.html time="00:12:00" %} If it does it just returns it; no need to go to the API. If it doesn't, it goes to the API and if it doesn't have it, it 404s, the API returns a promise to the store, this fired back to the component and everything's fine. If the store returns a promise or however you want to do that, in the public getter you could fire like a you could say this.state or setState as a {loading: true}. This where you would do your loading true back here. You fire off that read from the component and then when you fire it off you say this.state.loadingtrue and when it comes back you could this.state.loadingfalse and that's one way to handle a spinner. Someone was asking last time about a spinner and they were like, "well how would you do a spinner?" It actually was like a two week research project. Basically, this is how you do a spinner so that's where your false set state. 52 | 53 | Great so we got to the spinner question. But the spinner question is deeper, right? Because how does the component know that there's more data coming? The nice thing about this is that if recipes makes a call to the API ... oh my goodness! Async in React and Flux Google Slides. Wow! I don't know what just happened. But okay. 54 | 55 | Apparently, we're [inaudible 00:13:35] so the store they can return promises all the way down the line and the API, the last store can return a promise. The API comes back and it's successful, it's a two hundred and we go back the other way, the API passes the data back through the promises all the way to the component {% include skipper.html time="00:14:00" %} and we have no actions in the way. This is the way that Facebook is doing Flux. This is the way that they're doing Flux reads. 56 | 57 | Flux ... well let's put it into it gets a little more complicated. Here's the core problem: the core problem is the component in this case, is not allowed to talk to the store. It's just allowed to fire an action. Again, we're going to go through this at a high level and then we're going to go into code. We're going to go through all of this again so don't worry if you don't quite grasp it now but it's a high level. The components going to fire an action. These are all actions: start, success, failure and the next step is that the action creator are actually going to go ... you know what I'm going to do? Sorry. I should've done this in the first place. I apologize; hold on. We're going to copy the link and we're going to put it in gitter so if you go to Recipes Flux, Formidable Labs, Recipes Flux and then scroll down here and go to join chat, there are Facebook engineers in here to help you and I forgot to tell you this; I'm sorry. Hey, there's lots of you in here; great! 58 | 59 | Here you go: [ASYNC IN REACT & FLUX DECK](https://docs.google.com/presentation/d/1RQ7mrRUkga5J1wm8dvr_asZF-1bhJkHGuH-NLTdQhFs/edit?usp=sharing) there it is and so you should be able to come in there and delete everything. Hopefully that's read only. Great; okay. Let's ... there we go; nice everyone's coming in. Let's take a look at ... we've fired our action, let's say delete recipe. So when we say delete recipe, the first thing it's going to do is fire off ... the action creator is going to fire off a call to the API and it's going to fire off an action which is start. When it fires off start the store knows that, the stores are listening to that and the store says, "okay; hey he's loading it's true", right? There's something going on in the API {% include skipper.html time="00:16:00" %} and so we can then pass that down to the components and then the components can start a spinner. We've answered the question for the delete as well so we would then setState somewhere down the props is loading is true and some store we can then setState on the component and then start a spinner off. Now we're out of the API. 60 | 61 | Let's say that we had a failure, well once the API comes back, the data comes back from the API, goes to failure, then goes and the store catches the subscription and then passes everything down into the components that are listening. Same thing with success. If it comes back as a success then it's going to go to the store and it can be passed down. The store will do whatever it needs to do. If a store needs to update; it'll update itself and then fire emitChange down to recipes. This is an overview how to talk to an API with Flux. Have a good night. 62 | 63 | Anyway, let's look at that in code because obviously I'm not getting off that easy. If you haven't yet, I'll give you a minute and a half, two minutes to do this and then I'll take questions during that time. Go ahead and clone down ... if you haven't cloned down recipes Flux, do that, if you have cloned it down do get role and get the latest stuff. The latest stuff is Recipes Flux now contains all of its async logic and a server. 64 | 65 | ## Q&A: Sockets & Stores Listening to Stores 66 | 67 | Before I take questions, I'm going to give you a brief overview of how this server works, it's super-basic. It uses something called [LowDB](https://www.npmjs.com/package/lowdb). Which if you haven't used it, I highly recommend it. It's really neat. Basically what it is ... everyone know Lodash? Or Underscore?{% include skipper.html time="00:18:00" %} All right; so Lodash and Underscore it's basically a database that ... it's an object it's like a NoSQL database that you've query using Lodash. It's fast and it writes either in memory or writes to a flat file so that's what's back there if you're wondering. The plan with this and I'm pretty excited about this is to use Dropbox as the distributed ... if you don't want anyone to have anyone else's data, here's where their own recipes, then you can just write to a flat file with Dropbox.js, have people connect to your Dropbox and then just write it into a flat file and then we can distribute it with no SQL database without any backend. Pretty cool, right? 68 | 69 | Anyway, that's what you got going on there. Can I take some questions before we dig into this? 70 | 71 | **Speaker 2**: Can you show us how to [inaudible 00:18:58]? 72 | 73 | **Colin**: That's like maybe twenty-five minutes later. Yeah so the revolutionary thing that we talked about last time has been getting a lot of love. Anyone know what Radium is already? 74 | 75 | **Speaker 2**: Yeah 76 | 77 | **Colin**: Yeah, yeah so we built up over the past month or so and release it and it got some pretty good press and we're really excited about it. That's how Facebook's doing styles. It's pretty mind blowing there is no CSS anymore in their apps at all which is pretty mind blowing [Edit: ...at all... ...probably... ...in react... Ok I'm actually not as sure as I was.]. We're going to go over how to build an app without any CSS whatsoever. The answer sneak preview: is {% include skipper.html time="00:20:00" %} inline styles. We're going to teach you inline styles tonight. 78 | 79 | Any other questions? 80 | 81 | Okay; cool. 82 | 83 | **Speaker 3**: [inaudible 00:19:45] API called a photograph? 84 | 85 | **Colin**: Like load events like ... well load events you would use your actions for that. 86 | 87 | **Speaker 3**: Sure, but also {% include skipper.html time="00:20:00" %} when you want to [inaudible 00:20:04] 88 | 89 | **Colin**: Can you give me an example? 90 | 91 | **Speaker 3**: Streams. 92 | 93 | **Colin**: Streams like socket.io {% include skipper.html time="00:20:10" %} you're on your own; sorry. I actually don't know probably research on that I am not sure how to use socket.io in React {% include skipper.html time="00:20:17" %} I imagine that you could set that up ... David are you here? Where did David go? David, what do you think about socket.io {% include skipper.html time="00:20:25" %} ? I don't know how I would do it. 94 | 95 | I mean, what would you ... let's say theoretically you do it from the store and you use storage in ... 96 | 97 | I mean, that's the only way I can think of. Like doing it from the store and keeping the store in sync with the server and then just basically jettisoning ... David, right? And you'd kind of have to deal with complexity growing in stores that way. Yeah, so you would just stream back and forth to the store and then just make sure that your store was current with your ... and then whatever the socket event hits you should update your store and that will cause other problems in your app. You will basically when you want to have different stores, when the store grow, when the number stores grow, in between stores like that's just a Flux you're going to have to handle. 98 | 99 | Okay so let's take a look at the actual code to make this work. 100 | 101 | What we're going to do is we are going to walk through the code base and we're going to walk through the code base and this diagram at the same time. Let me get this ... okay. Let me get this going. 102 | 103 | Let's start out in recipe form. Recipe form is going to fire a {% include skipper.html time="00:22:00" %} create action so let's start there. This is line seventy-four of recipe-form.jsx. The first thing that we do is we fire ... the command to get this going if you don't have it going yet is gulp dev. 104 | 105 | Let's take a look at what this does if you were here last time you've seen it. It's a recipe app, it displays a list of recipes, you can click on each one you can get an item view. If you click on edit, that's what we're going to be looking at right now so you change these and they will now persist to the database. If I refresh, that's now persistent. If you delete all the recipes, they're gone, by the way because it's talking to a flat file so it's not refresh to get your state back like it was last time, these are persistent. If you do that, by the way, there is a db.json.bak for backup so if you delete all of your ... if you delete everything out of that flat file and you want it back then that's where you go. 106 | 107 | Let's start out in recipe form down here line seventy-four. We're going to say "recipe created" and {% include skipper.html time="00:24:00" %} it's going fire an action. It's going to say "ingredientcreated `_id: this.state._id`". That is going to ... let's look in action, that's going to fire an action. If you look in actions, in recipe actions you can see "ingredient_created". So there it is. It is dispatching it's calling this.dispatch so this is the slight change from last week. If you look through the McFly code, you know that the McFly code was just returning ... it was basically proxying data through the action into the store. This time, you're calling this.dispatch explicitly. The reason you're calling this.dispatch explicitly is because you now have control over when you call this.dispatcher and talk to the store. 108 | 109 | If you look up here, up top at a slightly more complex example: portions changed, input changed, ingredient deleted. These just call dispatch straightaway but, up here in "recipe created" it's actually using the request module from ... if you've used node you're familiar with that or you can use superagent or whatever you like. It's going to do a POST and it's going to send the data over and then when on end, when it comes back, it's then going to do this.dispatch with the action type "recipe created" and data. That way you're delaying talking to the store. What you've done there, in this line, this is lines ten through nineteen of "recipe actions" so once again, to make sure we're all on the same place, it's line ten through nineteen of "recipe actions" {% include skipper.html time="00:26:00" %} . Those lines right there are how you delay calling the dispatcher until you're acing call returned. Does this generally make sense? To do that? You guys kind of with me? 110 | 111 | That's like tentatively half. Let's keep going through the loop and then we'll come back up to the high level. We're in our actions and our actions are ... at this point, we've fired an action and we've fired "start" and "start" has hit the store and we've fired up all the API and then once it comes back, then we're going to fire success or failure. How do we know whether to fire success or failure? Well, like if error right; check for error, if it exists then send error and the data with it. 112 | 113 | In the store, let's look at stores "recipes stored" and let's look at "recipe created". At the top, again, the store is just an empty array or it could be an object it's just an in memory database like a collection in Backbone. The way that we're interfacing with it here ... if payload.actionType and again, let me go back here; how do we know it's payload.actionType? We define that here: action type: recipe_create. {% include skipper.html time="00:28:00" %} All of these we covered last time but I'll say this again just for clarity: every single store listens to every single action. What you're doing in these instatements is just checking if the type of the action that was fired corresponds to what the store is interested in knowing about. This is a recipe store, it's interested in recipe create events so it's going to fire this.recipe.data with data, create recipe is going to just push onto that array and then that's done, that operation being done, it's going to do an emit change. 114 | 115 | At that point, we're here, right? We've done the start event we've let it know that it's going, we've gotten a two hundred back from the API and the action created fires a success, the store subscribes to that, does whatever mutation with the new data that it needs to do and then it will emit change. Once it emit changes it is then letting the rest of the app know, "hey, there's new data you need to look at". That's the overall flow. Before I move on from there, any questions on that overall flow? Just dispatching an action immediately and then waiting until the API returns to fire off a subsequent action with the new data? Does the pattern generally make sense? 116 | 117 | **Speaker 4**: Question. Why do you fire the API from the action creator, why not from the stores? Isn't the action a proxy for [inaudible 00:29:54]? 118 | 119 | *[Edit: tldr; answer is that multiple stores may want the results. Stores just emit change events, they do not send data with that change event. Actions send the data with the success event.]* 120 | 121 | **Colin**: That's a great question. So for a get, to go back to a previous example, for a get, that's perfectly fine {% include skipper.html time="00:30:00" %}. 122 | 123 | But here's where you run into trouble: if you're doing a get then the store may use the public getter of another store, they use the public getter of another store, will go to an API that doesn't have it. That goes all the way through the back. That is the case if you're doing a get. But if you're doing like a delete or an update, then if the store does that itself ... let's say the component the user updates something and we have I know some of the more complex React apps have sixty to eighty stores, and so you have to assume that there's a dependency graph that stores need data from others and so we don't know in that case if the store mutates itself based on data because it's talking to the API itself, it would absolutely work if you have one, or two, or three stores that are all completely self-contained. They also work if you have sixty stores if you absolutely know that only this one store needs the data. I'd hate to talk to the API itself and no one else needs to be involved. 124 | 125 | That's fine, where the problem runs into is if when the API returns, ten different stores need to know about the data that came then you have the stores talking to each other and they really shouldn't know about each other. 126 | 127 | That's a problem. 128 | 129 | Does that make sense? 130 | 131 | **Speaker 4**: Sort of. 132 | 133 | **Colin**: If you want to ask a follow-up question feel free. 134 | 135 | **Speaker 4**: Just wondering the API doesn't exist on the back [inaudible 00:31:37] the best way database why can't proxy represent [inaudible 00:31:48] 136 | 137 | **Colin**: The store ... so David, I would say the stores would listen to each other right? They should have like listening for the {% include skipper.html time="00:32:00" %} ... like listening for the changes on their stores; would you agree with that? if they want to receive data from them automatically like they need data? 138 | 139 | David: Yeah so we have a concept of a store depending on another store. 140 | 141 | **Colin**: Potentially use a getter. 142 | 143 | David: Yeah. 144 | 145 | **Colin**: Yes. 146 | 147 | **Speaker 4**: In that case doesn't it make sense to have a proxy and the backend still be like [inaudible 00:32:31]? 148 | 149 | **Colin**: That's interesting model; David what do you think of that? 150 | 151 | David: I don't know. 152 | 153 | **Colin**: I'd have to think about it. You could definitely start an argument. There's a lot of ... Today, there's a chat-room called React to Flux I don't know if anybody ... reactaslack which is a slack channel for people who want it. Today there was just this epic conversation; basically, a lot of questions like that. There are maybe twenty, thirty Flux applications at this point with different opinions. It actually, if it works for your app, do it. I think at this point, what I'm seeing is that Flux is small enough, the dispatcher is small enough that you could pretty much roll your own implementation of flux per project. It may make sense to do so given data constraints. This is the one that works for n number of stores and we know it does. But I think that's an interesting idea. In a way- 154 | 155 | **Speaker 4**: I have done this with a RESTful API 156 | 157 | **Colin**: With react? Yeah. Works fine? Yeah; great. 158 | 159 | **Speaker 4**: Mine is a small test application {% include skipper.html time="00:36:00" %} {% include skipper.html time="00:36:00" %} {% include skipper.html time="00:46:00" %} [inaudible 00:33:47] 160 | 161 | **Colin**: Sure, sure, sure. You bring up a good point because what is going on here if not in effect the action creator is acting as {% include skipper.html time="00:34:00" %} a dispatcher for an event. It effectively is exactly what you're talking about. It's saying, "hey I've changed; you need to get data from here" and it's sending the data over. The only thing that's different and this is actually quite a big difference, is that, if this were to happen in the store, the store would say, "hey, I've changed but I know what I need and so I'm going to go use the public getters to go get it". But here, this store is a kind of special type of store where it doesn't have any needs, it just sendings data. When it emits an event, no one is reaching into it to get the data, it actually, the data comes with it. 162 | 163 | **Speaker 4**: In a way the action creator is the story proxy that I'm talking about. 164 | 165 | **Colin**: It is really, yes, it is a store proxy. I think that's the difference though, when the other stores update, they do not send any data with them. That's the big difference. When you have sixty stores and they say, "hey, I've changed"; anybody who's interested can come listen to it. But that's not the case if you have an API. If you haven't actually come back with an API, it comes back with the response body and so the response body is then passed on to any store who wants it rather than the API saying, "hey, I've changed" and then all of the stores making API calls. That would not be good. 166 | 167 | ... 168 | 169 | I'm sure you've all cloned down and we're probably about ready to move on {% include skipper.html time="00:36:16" %} let's take a look just one more time at this flow because this is pretty important, generally. I'll see if I can explain it a different way; I'll use a different set of examples this time and if you haven't gotten it, hopefully this is the time. 170 | 171 | Once again, just to go back to basics, this is what we did last week. Component fires and uses a public getter so recipe says, "hey, I need to know what the recipes are". Get some stuff out of the store, the store gives the data back. OK to use a public getter because we're not going to mutate the data. We don't want to mutate in the store from the component. The component doesn't know how many stores there are, it just knows that it's getting data. It knows what it needs, and it knows what it gets. 172 | 173 | Also, if you need to go through an API, it's the same thing. You can have as many stores as you want, they can all return promises, the data comes back and the component gets it. It's where we need to do it on optimistic update, for instance. Let's talk about the update piece, not the create case this time. We fire, the component says, "hey," ... let's say bananas, that should be easy to find. That is now consistent and let's make sure that's back there in the db. Yeah, there it is, okay so that made it to our db. That write went all the way through. {% include skipper.html time="00:38:00" %} 174 | 175 | Where it becomes necessary to dispatch two separate events is when we want to do an optimistic update in the user interface. We want to say, "well we know the store is changing, we want the user interface to reflect that without any latency whatsoever". We want that start event to go through and maybe that event carries data with it. It triggers a change in the store and that change goes all the way through to recipes. It may be the case that when an API comes back that it stays successfully nothing changed, then the user wouldn't even know. It would just look like it happened with zero latency. But it could be saved. 176 | 177 | It's kind of like that Google Docs effect when you're typing and it's just saving in the background, you don't really have any latency on the client's side. Actually, it is exactly that but it's really easy to implement at this time. 178 | 179 | Let's take about ten minutes here. What I'd like you to try to do is I'd like you to try to write something to the db. I'd like you to do the following; let's walk through it together: so write something to the DB from the UI. We will ... and I promise after this we're going to do something insanely fun, this is not so fun but necessary and then after this we will do a lot of fun stuff. 180 | 181 | The first thing we need to is we need to create a new handler in index.js and that's going to be in server. There's express back there so first thing you could do is create a new handler in server {% include skipper.html time="00:40:00" %} for a new recipe route and then create ... and then basically follow the pattern of what files that would be. That would be recipe ... client, components, client actions, recipe, recipe actions and no need to create new actions files or new storage. You can use recipe actions and recipe stores that way you don't have to worry about inquiring into files, that's prone to typos. Then client stores and recipe, recipe store and then arbitrary component fires an action. Here's what's going to happen: arbitrary component fires an action with arbitrary data. We send that data to the server and save it to disk using load db. If you want load db, lowddb is here, you can do npm, npm lowddb. There you go okay, npm lowddb, this is the API for that. 182 | 183 | Then we will save that to disc and then we will return a success {% include skipper.html time="00:42:00" %} and then fire the body to the store, update the store, and then emit change. Listen for and recipestore.mixin. There's a little bit of magic to connect the store to the component and that's in recipestore.mixin. Listen for a change on the store, six: re-render and I'm going to leave that up here. I'm going to give you guys maybe ten minutes to work through this and hopefully at the end of ten minutes ... even if we're not all the way there, you'll have worked through it enough that we'll have some question that you can dig in and then we will move on to the really exciting stuff. All right talk to you in maybe ten minutes. Feel free to work in pairs on this as well. 184 | 185 | Okay everyone; I'll take some questions now. I'll take some questions now. 186 | 187 | Hopefully this has been thorough enough this was a huge, {% include skipper.html time="00:44:00" %} upsetting question in the community and I wanted to make sure that you all go back with this. We've got some working code here and we've got some working examples to take home as well. Anybody get it working; was that enough time? No. Not at all. Did you get it? Anyone want to ... any questions to follow up on that once you dug in? Any generated questions? 188 | 189 | Let's switch to the fun stuff. It is 8 pm we are going to talk about Radium for about half an hour and then we're going to have everyone spend about a half an hour doing the craziest things you can think of with it and then we're going to do show and tell. That's where we're going now. Let me save that. 190 | 191 | Another thing to show your boss if you want to write lots more React. One more company just did a code only rewrite of a non-trivial production enterprise application in React because it's cheaper and more maintainable to do that in the long run than to keep the graph of angular/ backbone logic that we have now. Pretty exciting to watch everybody {% include skipper.html time="00:46:00" %} continue to rewrite everything like air b&b did. 192 | 193 | **Speaker 6**: Wired.com 194 | 195 | **Colin**: Wired, BBC, HipChat now, Netflix yeah lots of great ones. 196 | 197 | ## Radium 198 | 199 | Okay, so if you would point your web browser to [the following address](http://projects.formidablelabs.com/radium/) I'm going to walk you through what Radium is, what problems it solves and then we're going to play with it a whole bunch. We've been incredibly excited about this in our shop because we have run into a lot of the problems that Christopher Chedeau ran into when he was talking about doing inline styles so I want to run you through this presentation very briefly if you've seen it. You can just Google CSS and JS not going to read the whole thing. 200 | 201 | Basically the overview was, "hey, there are lots of problems with large CSS code bases". For instance, so Bootstrap introduces about six hundred globals and these are practices that we don't follow in JS, we would never accept six hundred globals in JS but somehow we approve of CSS such a practice, there's no modularization and there's no great way to share constants. For instance, it's not really possible to isolate. 202 | 203 | I was talking to someone on a very large project and they were describing CSS recently. It was when we were just beginning to evaluate the solution versus what we had and he said, "well, what I kind of do is like {% include skipper.html time="00:48:00" %} it's kind of like there's all these layers of what people have done before and then there's like your button and you have to make sure your code is in the way of all their code." I was like, "all right well then" we probably replicate all of the problems that Facebook has here because you've got waves and waves of these it's cascade, right? The cascade is a whole bunch of globals and your globals haave to be in front of their globals and that's what cascade it. It's terrible! 204 | 205 | The result of this exploration that Facebook went through was to do everything in line styles. Specifically, do everything in in line styles it's in JS, it's modularized, and then you make an object and then you apply that object to make in line styles. This solution, however, when we began to build with it it's not complete. 206 | 207 | Can anyone think of some of the cases that are not covered? What kind of problems would you run into? What kind of things would we become used to doing in CSS that you would not be able to do? 208 | 209 | **Speaker 7**: Hover. 210 | 211 | **Colin**: Hover, yeah hold and hover that's great. Any pseudo state: action, focus, blur all of that yeah what else? 212 | 213 | **Speaker 8**: Media queries. 214 | 215 | **Colin**: Media queries, yes absolutely so media queries are a great example because it's basically like an if statement. 216 | 217 | Oh, here's a really good one this is pretty funny. This is CSS ... this is pretty funny on a number of levels. Has anyone seen the spec for in range? Anyone seen this? In range? It's like media queries but how much logic can we pull into CSS? This is not tenable in the long term because you know we're going to touch it with JS anyway {% include skipper.html time="00:50:00" %} . We're going to get in there, we're going to rip it out and we're going to put some other value back in for max because there's some Edge case and anyway. We're going touch this anyway, right? We're not going to leave this alone not any more but having in range in the CSS means even more traversal of the CSS of the DOM so the more logic we pull into CSS the slower it gets because there's lots of DOM traversal. 218 | 219 | Media queries were a great example of that. They're basically they're if blocks. It's like this. It's window ... hold on, let me zoom in so it's window.interwidth. There you go, 1886; it's an if block. It's an if block given that condition. This in JS is trivial but in CSS this is like reams and reams of blog. This is ... it's not better, it's just what we were used to in CSS. 220 | 221 | This is pretty funny: Dionne Almer tweeted out about this when React first went up and this is my favorite response. This is Alex Russell who is on the standards committee and if you're watching I am so sorry we went full bore in destruction. He's on W3 standards committee so building CSS4. Anyway, one of my favorite reactions ladies and gentleman the community reaction was really good because there are a whole bunch of problems. 222 | 223 | Check this out: {% include skipper.html time="00:52:00" %} that's really smooth! Check it out: this.state.ww divided by ten that's computation. Computation in styles because it's just JavaScript. You could do ternary operators, functions, you can modularize all of the objects. You can do everything you did in JS and what Radium's doing is handling all of the states that are ... so go to the Formidable Labs blog post [Launching Radium](http://formidablelabs.com/blog/2015/03/01/launching-radium/) you can see this and it's not Radium yet. We're maintaining Radium we hope it's the one it is come about with conversations with Christopher Chedeau who wrote CSS and JS he's coming up with a lot of the Facebook methodology and a few other people at Facebook as well so we're trying to handle all of the Edge spaces that you're going to run into inline styles. It covers, it focuse and hover, check it out it's actually way more performing to do in line styles because React is diving them for you. 224 | 225 | If you only change one thing in line styles you can put thousands of buttons on and change things on JavaScript events even while those events are still super performant and you can recompute a ranDOM background any button on hover that you want; it's instantaneous. It's wildly performing and we're going to have ... in two weeks we have a blog that's coming out with tons of graphs about just how performant this is over CSS as well [Edit: Ok it took way longer and we're still working on it]. Anyway and that is not the case that it competes with CSS if you're not doing tons of computation. Again, this is like given the fact that we're using modern web apps and we'd like to be doing tons of complex computation all the time it would be better to do this with Radium but obviously if it's just a head to head comparison of like using JS and doing it with CSS, CSS gives you extremely performant in itself. It's just over computational edge space that we want to do all the time that are really not. 226 | 227 | Let's go through some of the basics. First off, you can modularize it. You can take all of your {% include skipper.html time="00:54:00" %} styles put them in an object, put them elsewhere in your app. You can ... here's an example of this part right here just takes care of getting the text in and these just set the value of the counter so don't worry about that part but what we'll look at is right here so the background color is a function of state. As we change the counter, we're re-computing the color every time and we're also as we change the state, we're re-computing the padding every time. That means that we can do with two lines we can do this: we can change the text and we can change the styles and it's all re-computed on the fly every time the state changes. 228 | 229 | Just think about all of your style sheets being re-computed. Think about this: think about re-computing ... think about having an I-frame, a widget, an I-frame widget inside of your site, that you post message into. Communicate into the I-frame and when you communicate into the iFrame you send an object in. It receives that object and applies it as a style guide and re-computes the entire app based on that object. That's like pretty much trivial because it's just an object and you're styling the app it's could be this.styles.blue or this.styles.buttonColor so you can just restyle the entire app and from like asynchronously you require an object, bring it back re-compute the entire app based on that. Things that were pretty inconceivable are possible. 230 | 231 | One of the other things that Radium handles is ... or I should say, that you could do with in line styles with some code that Radium can make even easier for you to {% include skipper.html time="00:56:00" %} make it more accessible for you that's more accurate. All this stuff you can do on your own if you want to obviously, you can read the Radium source but these are some helpers. The Radium button time equals warning let's try time equals primary and when we re-compute that it's just going to change it to blue and we're using props there to do explicit modifiers. 232 | 233 | Anyone ... if you all know like Bootstrap alerts where it's like where class equals alert, alert-danger, right? Class equals alert, alert-primary. It's not necessarily clear in your CSS especially as your code base grows that alert-primary and alert-danger shouldn't be applied to the same element. Block out a modifier with basically this is done semantically like Ben, you guys have naming it, you use naming conventions for this now. There's no way to explicitly say that this it should be alert or default or warning or success those aren't explicit the way that that's done is with naming conventions. But here, you can do explicit modifiers so you can say time equals primary and primary does down to Radium and it will apply that one which means you can modify the state of a button programmatically as well. 234 | 235 | It also handles all of the events in CSS. This is done with JS it's just done with the JS event listener. It's just on hover again, we've got something coming up in two weeks on the exact performance of this graph but it's totally performing to do this. It's totally performing to have thousands of things {% include skipper.html time="00:58:00" %} on page with JS listeners it's fine. Then you can do ... for progressive enhancement, check it out: what is your CSS? Well, it's a turner rig like modernize it like box so does that exist? 236 | 237 | True or false if it does then display Flex, if it doesn't display what? Then you can even go a step further and just have entirely different styles. If Modernizing.FlexBox doesn't exist then you would use like a legacy grid. If FlexBox does exist then you use the latest grid and just use the latest stuff. Basically that's like the bottom line here. What you get with in line styles is you get computations and you get really, really, really powerful logic. All the logic you're used to using in your normal app. This is really a game changer so check out this would seem very scary because we are used to modularizing our CSS. There's all these best practices around modularizing your styles. 238 | 239 | But in fact, this isn't all that different. The problem way back in the '90s was that the styles were actually in line but we all know we're not going to do that. It's not like you're going to have all of the styles actually on the elements, we're already, even here even in these examples I'm showing that I'm extracting into an object ... you're going to require them just like anything else so like having them externally, you still have them externally. It's still separation concerned, it's still modularized. It's just that you get computations. Anyway, this one thing right? Here's setting font size, no line whatsoever, setting font size is a function of window width so you're changing your styles. {% include skipper.html time="01:00:00" %} Otherwise you would have to go into CSS and change the CSS value. 240 | 241 | Actually, let me put it to the crowd: how would you do this? How would you do this in CSS? What would you do? If you wanted to do it? 242 | 243 | **Speaker 9**: Use viewport width 244 | 245 | **Colin**: Viewport width. 246 | 247 | **Speaker 9**: A VW instead of pixel. 248 | 249 | **Colin**: Yeah exactly, yeah. I think that's in the same pattern of bringing all of the values, and all of the globals and all of the logic into the style sheet, like media queries, being ifs and other things like that. We need the spec too, the spec is the slow train that's following behind us and fortunately now it's like we don't have to wait for the spec to give us things like that we want, we can just use logic. There's another example I'll throw this up in the chat and you can try this locally. It's a chat now. 250 | 251 | This is an animations on letters, per letter animation. There are entire libraries dedicated to jig rig plugins this logic isn't a four hundred line jig rig plugin? This is all it takes to do that. Some of the equivalent stuff for that is lettering JS, for instance and lettering JS is ... it's helping you do things like herding but it has to go down to the letter but {% include skipper.html time="01:02:00" %} finding great control over letters is also performant over letters also possible. 252 | 253 | People asked a few really good questions when we first released this and then we can start playing with it. One of them was: if you're using in line styles let's say that you have ... here's one of the things that no one brought up that you can't do: you can't do it with in line styles let's say I have a div and I have an ID of foo and I have fifteen paragraph tags littered at some level of nesting and I don't know where they are but I need all of the paragraph tags inside of me to be styled like this. That's why we have all these complex selectors in CSS. So how do you replicate that? 254 | 255 | Well, most of the time ... as we started working with this, we realized a few things. One was that we could modularize our styles and require them in subcomponents so if you have access to the element you can just apply a different in line style to it. For instance, if you are creating a list item, let's say you are creating ten list items into each. As you iterate it over the each, you would simply apply styles computed or not or static to those list items that you created. That's scenario number one. 256 | 257 | Scenario number two is that you don't have any access. Maybe you're loading html asynchronously or you're not going to be able to get them. The fix for that, is using a style tag. You can programmatically inject style tags into the DOM. Like in the head that you would use like style and you would have a whole bunch of CSS in there, you could programmatically inject a style tag into the DOM. Put a whole bunch CSS in it like a key frame navigation and then a reference tag in line {% include skipper.html time="01:04:00" %} style. 258 | 259 | The CSS then just becomes an annotation it's like a little extra when you need it rather than ... and that even having a style sheet like it, I don't see any reason you couldn't continue to use a style sheet but this might reduce ninety percent of the need for it. If there are some things that you cannot do under any circumstance, then okay use a style sheet. But here's the thing: even this, even using style tags inside of the DOM, because it's done programmatically, allows you to reference specifically what React elements you want and so you can do specific selectors with CSS that won't bleed into other DOM elements. It's scoped to React components. 260 | 261 | We're working on server side rendering right now, I'm going to give you guys something to fork and we can start playing and so we'll do two things simultaneously. I'm going to give you the place to start playing which is, here's how you do it: go to this blog post and I'll send this blog post out in the chat. Scroll down and fork any of the in line styles that you want. 262 | 263 | [Here are a couple things meetup attendees built in just a few minutes](https://twitter.com/coli/status/573385816107458563). 264 | 265 | Come up with a crazy idea. We're going to share them at the end, I hope we have maybe four, five, six people able to share ideas that they have. Compute something, create like what would mean to do responsive but with this. What would it be to change all of your styles programmatically? To change all of your text sizes on ... I create little games like this too, I mean much better for game {% include skipper.html time="01:06:00" %}. Anyway, no. 266 | 267 | I'll leave it up to you what you do with it but first I'll take questions and then we'll move on. But I can't emphasize the place, as crazy as this is, and this is a lot. This is actually how Facebook.com is being built. You can go see, listen Christoper Chedeau talk about the way in which they [inaudible 01:06:31]. 268 | 269 | David, I know not everything on Facebook is done like this, but do you know what Christopher had been able to? 270 | 271 | David: My guess is React Native stuff {% include skipper.html time="01:06:42" %} 272 | 273 | **Colin**: React Native stuff, yeah. 274 | 275 | David: I'm not sure how far away from the core React team it's gotten {% include skipper.html time="01:06:46" %} 276 | 277 | **Colin**: So anyway questions, comments, pitchforks? Go ahead. 278 | 279 | **Speaker 10**: Single elements like before and after will you actually use those with this? 280 | 281 | **Colin**: Yes because you're doing a few things. Let's just say you could programmatically insert an element. Because you have JS you could just insert like a span and then style it. So there's one. You could just do that from the component. Another ... let's take ... is nth child is that like comparable? Or first/last. Let's say you're rendering and you have first/last. Well how do you get first/last ... if you're rendering first and last simply become just like logic. What's the issue to find the last element of the array and then ... I think that's like .... these things become ... like the media queries become pretty trivial if you're computing based on the inter-web, you could do all these globals and I think in a similar way, a lot {% include skipper.html time="01:08:00" %} of the things that were hard about CSS with logic like nth child, it's like, "well whatever the nth child is, just get it". That would generally be my answer. 282 | 283 | If you found something, it'd be interesting please contact me if you think of something that breaks that, because again we're using style tags I'm trying to compile all of the instances of when you're really going to want it but if you have something that breaks that, please tell me because we're thinking through this now it's pretty green fields for us right now. 284 | 285 | **Speaker 11**: Two questions: How do combine [inaudible 01:08:35] design? 286 | 287 | **Colin**: That's up to you because anywhere in JS because again it's merging objects. Like it's not only can you combine two classes together, you can do whatever you want at any time. 288 | 289 | **Speaker 11**: Second: how would you go about theming I want to change all the divs and then write inside of my page to [inaudible 01:09:00] 290 | 291 | **Colin**: The question was: how do you theme your page? What you would do is you would have a module of this big object that would style globals and you would have everything inherit from those properties. You would have style globals.main color, style globals.secondary color and you could just swap it out for different colors and your app would just recompute. You could actually do that live in browser; you could just actually manipulate the styles object and emit events and then your components would re-render or recompute everything. One of the things one of our guys is building right now is an in-browser redesigner. Where it's a re-compute. You can just change this to blue and change that. It's like a site editor but like ... you could basically on it's own sort of like the crazy go through a website like site editors but like pretty trivially because you can change stuff in the browser itself from form fields. Set the styles programmatically the color of an input, the color factor input. {% include skipper.html time="01:10:00" %} 292 | 293 | In other words, let me say this slower: put a hex code into the input, make that your sidebar. The value can now come from anywhere so- 294 | 295 | **Speaker 11**: Global variable object? 296 | 297 | **Colin**: Sure maybe global variables like a global variable object that is just required as a node module into only the files that you want it to be required into and that global ... if you haven't required that global into a component and that component loads on the page, that button will be fresh. 298 | 299 | That is a html button with nothing on it. You have no more globals and you have no more ... in other words, when you need to customize bootstrap, it is just terrible because if you want to do anything custom you're then fighting it. In this case, if you started with a component that didn't require the like the bootstrap module for that, it would simply be completely unstyled and you could start from scratch and then you could slowly ween your app off of something like that- 300 | 301 | **Speaker 11**: [inaudible 01:10:59] not to force itself onto anything require. 302 | 303 | **Colin**: Has not been [inaudible 01:11:02] no, well, there's no cascade. There's no cascade and there's no style sheets. 304 | 305 | **Speaker 12**: Three things: one is with CSS like bootstrap for example, that over six hundred globals that you talked about, that's an example of an entire bootstrap [inaudible 01:11:20] downloaded from bootstrap but they have the customized code they'd gone through and select any information you want. Not necessarily [inaudible 01:11:29] but then also to say that the lack of name spacing you've got an example of bad CSS. Like earlier, the person who was fighting with CSS. 306 | 307 | **Colin**: The point I was making was the lack of name spacing is the default in CSS. There's no name spacing is done semantically. You do it by saying, "button-danger-alternate". {% include skipper.html time="01:12:00" %} You use semantic, you use naming to define the scope of what ... it's really your only option. 308 | 309 | **Speaker 12**: You could do that but you could also say if you have a widget a class high widget, you can say mywidgetspace whatever elements are setup the specs in the space [inaudible 01:12:24] of that class. So what you're talking about with the JavaScript stuff essentially is that the client's scoping for anything inside of that particular setting. 310 | 311 | **Colin**: Well yes as a byproduct you get that for free because it's JavaScript. Yes, you do, but that's not what this is about. This is about ... I can't argue about the ... I couldn't argue for this for a static website. This is not a modularization this is about computation line. This is about recomputing the styles, this is about saying that this ... the width of this sidebar should be a function of the text inside of it. That's something that I ... how hard that's very hard. These are things that are very hard to do with CSS but it's the stuff you want to do in modern web apps that's based on computation that this is an argument for. This is not an argument ... [inaudible 01:13:17] like web apps and it's really for style content so I would argue with that just on the basis of modularization but you do get that for free. 312 | 313 | **Speaker 12**: The last one was: do you think CSS4 will catch up? 314 | 315 | **Colin**: No. There is no way for CSS to catch up ever and the reason is that the spec will never ... in pulling things like colon in-range into CSS the problem with that is of course is that like where does that end? You're pulling more and more of this ... you're trying to make it declarative. You're trying to make more and more of the layout declarative but it's not expressive enough because it's declarative. It's never ... CSS is no way never going to be expressive enough to {% include skipper.html time="01:14:00" %} say something like ... take a modernizer sample. 316 | 317 | [Disco Stalin ]Nice; I think! Well that's a valid reaction too. It's a little bit cranky but valid. By the way, speaking of a little bit cranky but valid, this is what the programming gods bestowed upon me at my office the morning after I released this: that was a guy chainsawing down a tree right outside my window. I knew immediately that this was trouble. Came in the next morning and my quiet time was disrupted. 318 | 319 | What's that? 320 | 321 | **Speaker 13**: Are you sure that wasn't Paul Irish? 322 | 323 | [Edit: https://twitter.com/coli/status/583761681261596673] 324 | 325 | **Colin**: Honestly, I am probably not prepared for the hate that's about to come down but yes, I'm saying that CSS is bad, CSS is never catching up and that it's over. That's what I'm saying. 326 | 327 | **Speaker 14**: Do you think you'll use this in combination with stylesheets. 328 | 329 | **Colin**: I think that everyone will probably still use style sheets. I think [inaudible 01:15:08] animations are great you've hardware accelerated CSS3 transitions, it's good. There's good stuff about that and there's good stuff that you can do with selectors, I just think this is going to get rid of ninety-five percent of the need for it and that's a good thing. 330 | 331 | **Speaker 15**: My first question is where are you drawing the line in the sand? If you are still using CSS [inaudible 01:15:33] content this is the best thing ever? 332 | 333 | **Colin**: It is for dynamic not necessarily for- 334 | 335 | **Speaker 15**: Are you generally using a beta style sheet or are you just putting that on there somewhere [inaudible 01:15:45]? 336 | 337 | **Colin**: No actually we have two apps going that have zero style sheets. It's all like this. It's just everything's swappable. 338 | 339 | **Speaker 15**: How do you create structure? {% include skipper.html time="01:16:00" %} Is there some base object that defines the global theme or where? 340 | 341 | **Colin**: You could. You could do whatever you want. I would have ... I would modularize it like you modularize Sass. Do fonts you know? I mean ... Sass ... one great thing that came out of Sass was that because you could do import you could import, you could do variables. Copy your Sass files. Take what you have in Sass with all the variables and all that logic and all that stuff but rather than compiling the Sass down to something static and declarative just keep it in logic and then you can continue to change those variable. It's like that, right? It's like you kept Sass on the client you could just keep changing out the variables whenever you wanted to. 342 | 343 | We all want it. We all really ... we can't admit that we want it yet now I have it now we can, right. 344 | 345 | **Speaker 16**: This might be trivial just general web application development but, for instance, you guys worked on Walmart at Formidable Labs. Say a product detail page. There's lots of mini web applications within a page like that with individual components. But a site still needs to be highly SEO searchable, all this. Are you delivering up ... are you implementing these in like enterprise clients? What are you delivering up as content for search engines? 346 | 347 | **Colin**: You're talking about having a page server side rendering generally like a [inaudible 01:17:30] case? 348 | 349 | **Speaker 16**: Yeah. 350 | 351 | **Colin**: Can we take that as a specific example? One of the things that we're going to be ... one of the things we're working at right now, it might actually already be done, I'm not sure. Is you can compile the app on the server side how to inject the in line styles and then send that over in React, render the string and then all of you app is just there. But, if you do that, then your media queries {% include skipper.html time="01:18:00" %} don't work and your JavaScript states don't work and so your app is hydrated with JavaScript right? That's a problem that we're solving right now. That's something we're in the middle of. 352 | 353 | **Speaker 16**: Are you basically sorting out like a backend server robot. Are you serving up the static content in its initial or something to that? 354 | 355 | **Colin**: I should add that while Facebook is doing this, this it took us two months just to write Radium. We have not yet converted. We have enterprise clients that we're starting to do projects on with this but we have not ... it's not like we've converted every old project to it not yet. What was the question just for clarity? 356 | 357 | **Speaker 16**: Not even so much related to CSS styling but more just in general what are you serving up to a robot that's crawling pages like this? 358 | 359 | **Colin**: You're serving up React to render the string and it will render your entire application. We use Reactor router and then React router will match a route and then it will call to render the string. 360 | 361 | **Speaker 16**: I'm sure there are a lot of articles covering this. 362 | 363 | **Colin**: It's a good question. Especially because of what we were talking about today with the async stuff. You can call render a string and serve up the static page. 364 | 365 | One of the things we're working on is it looks like the way to do this is to take all the in line styles and compile those to one major style sheet in the head that would then go away when the app actually loads up the JavaScript and that would just get all the in line styles to compute it. Does that make sense? You would just compile all the styles to CSS. It's basically like you just rendered one state. {% include skipper.html time="01:20:00" %} The first state's done like that then every subsequent state is done recomputed. 366 | 367 | **Speaker 16**: [inaudible 01:20:08] all the JS states accessibility? 368 | 369 | **Colin**: Good question. Can you actually go to Radium and open a ticket with that question and accessibility? Both of those? I want to make sure we get back to you in a thorough way. 370 | 371 | **Speaker 17**: Can you talk a little bit about perf [inaudible 01:20:34]? 372 | 373 | **Colin**: No but when we ... so what we are doing is we're generating tens of thousands of elements and then we're using dentals on a fifteen inch retina Mac and so we'll release the device it was done on, the browsers that it was done in and the times for all of those and graphs of all those and how many buttons it was. The idea that I think was going to end up happening for this is that we're going to actually write a small app that's going to allow you to do these yourself. 374 | 375 | 376 | 377 | **Speaker 17**: [inaudible 01:21:13] 378 | 379 | **Colin**: How long it took the scripts to run for instance? How much RAM consumption? 380 | 381 | **Speaker 17**: [inaudible 01:21:29] 382 | 383 | **Colin**: In two weeks we'll have covered all of that but we haven't covered it yet. Can you open a ticket on Radium for that? Please open a thorough, perf ticket it would be a huge benefit just complete every hole that you can poke in it that you would want to see in that post, dump it in there and we'll try to answer every one.{% include skipper.html time="01:22:00" %} 384 | 385 | **Speaker 17**: [inaudible 01:22:07] 386 | 387 | **Colin**: It definitely is. 388 | 389 | **Speaker 17**: [inaudible 01:22:14] 390 | 391 | **Colin**: Short side rendering won't be in line styles, it will be probably ... we're still attacking this but it looks like a single tag, a single style tag in the head of the html that will be rendered. It will be computed and then put in the head and then sent over as CSS. 392 | 393 | **Speaker 17**: [inaudible 01:22:38] 394 | 395 | **Colin**: That's a good question. Server side rendering with focus and blur and all that stuff we're attacking now. We're going to get, we've got to get it but it's going to take four more weeks probably. 396 | 397 | **Speaker 18**: [inaudible 01:23:02] 398 | 399 | **Colin**: Yeah that's what we're same as you. I would say that we're probably going to have to release a blog post one blog post I'm going to add that to my to do list now. We'll release a post also on specifically server side. It's not done yet. We're not quite done with the hard problems on server side yet but we're going to get there. 400 | 401 | Go ahead. 402 | 403 | **Speaker 19**: Just out of curiosity [inaudible 01:23:55] when you're writing the CSS and JavaScript you get [inaudible 01:23:58] 404 | 405 | **Colin**: What do {% include skipper.html time="01:24:00" %} you mean? 406 | 407 | **Speaker 19**: If you were to type a property and you didn't know the span of all the values it can give [inaudible 01:24:08] 408 | 409 | **Colin**: Oh yeah right. 410 | 411 | **Speaker 19**: How would you find out ... is it known, is it hidden or is it transparent? 412 | 413 | **Colin**: I'm sure someone has written some line plugin but no, no there's none. 414 | 415 | **Speaker 19**: [inaudible 01:24:19] 416 | 417 | **Colin**: Vendor prefixing is also an open ticket on Radium. We're also working on solving that. There aren't a ton of problems left and it's been only like a month and a half that Alex has been ... I should mention this explicitly: Alex Lande [inaudible 01:24:43] following on Twitter is a brilliant developer and he just did all of the CSS for a very, very large site that you guys [inaudible 01:24:50] work on and he wrote multiraneal and it's really brilliant. It's brilliant interface and we're pretty [inaudible 01:25:05] he's really only had a month and a half with this problem and there are some big problems left to solve. 418 | 419 | **Speaker 20**: [inaudible 01:25:12] 420 | 421 | **Colin**: Yeah I mean Alex just ... where's Ryan? Is he still here? Or did he just take off? 422 | 423 | Okay that works. 424 | 425 | Alex just did the [inaudible 01:25:33] Alex Lande just did all of the CSS styles for the new Walmart.com and you go to the new one it's beautiful, it's super-slick it was a massive, massive, massive project. He ran into all the same problems Christopher Chedeau did it and so we got excited about this about a month and a half ago. We looked at this and we're like, "man! There is no going back". It's like padding, turnering, like {% include skipper.html time="01:26:00" %} padding and function you've never ... how could you ever go back? Once you have that kind of logic in your web app. Again, not essential if you're displaying an item page maybe if it's just a template. If it's a template, it's a template; you just put a style sheet and a template. It's hardly ever actually like that. 426 | 427 | **Speaker 21**: What about a hardware like bootstrap where you can use that project to project is this reusable across projects? 428 | 429 | **Colin**: Looking for contributors we're re-implementing all of bootstrap in Radium and it will be themable in that way and yes I mean because you can just use objects you can. Yes, because you can use objects you could just create any number of combination of those objects that you want and reuse them from project to project. Require them to be on npm, require on npm someone's theme use it on your site it's a whole bunch of colors. npm install like freestyles and then just like grab some colors from a designer, use their stuff, use the buttons, use the padding, yeah. Or like npm installed some button and just grab the styles with that button require it in, put it on the button, style it. Done. 430 | 431 | **Speaker 22**: Just curious how this works in Spec Elements? 432 | 433 | **Colin**: In Spec Element? Great question; what about it? 434 | 435 | **Speaker 22**: With CSS source maps, will it work with that? 436 | 437 | **Colin**: Yeah absolutely so if you're ... let's take the first case, it's actually going to blow up if there's something invalid, it will blow up either tin build styles which is where you're taking that object {% include skipper.html time="01:28:00" %} and you're putting it on that element and let's look briefly at the interface while we're talking about this. Where are the opportunities for this to blow up? It could ... you're requiring a big object here let's say the object was actually in you CSS file and it was not modularized so if it blew it would blow up just in JS. Let's say this function blew up it would just say "this.state.ww is not find". That would be that. It would just be which would blow up in your JS. I guess that if you gave React something, if you gave the style tag something that it didn't know what to do with, then React would blow up and say "hey you need to [inaudible 01:28:54] casing properly so yeah I don't know what to do with this". That's another place it would blow up. Otherwise you could set a break point in the middle of your files if you wanted to. 438 | 439 | **Speaker 23**: [inaudible 01:29:08] 440 | 441 | **Colin**: Like setting width? Yeah absolutely you can. 442 | 443 | **Speaker 23**: [inaudible 01:29:19] 444 | 445 | **Colin**: Actually can someone do that right now while I'm talking? Can someone make- 446 | 447 | **Speaker 23**: [inaudible 01:29:25] 448 | 449 | **Colin**: Here's the text size. Try this one with an image. There you go; you can dynamically resize an image based on it's container. That's- 450 | 451 | **Speaker 23**: Then make it stop when it hit a max or something? 452 | 453 | **Colin**: Absolutely. [inaudible 01:29:51] 454 | 455 | **Speaker 24**: [inaudible 01:29:55]what {% include skipper.html time="01:30:00" %} bower? 456 | 457 | **Colin**: What's bower it's a package manager. What is it? What is npm? What is package management? What's brew? 458 | 459 | Someone make an example of this and then you can send it back to me, send the clip for the code pen, click fork, and then send it back to me over getter and I will put it up and show everyone. 460 | 461 | **Speaker 25**: [inaudible 01:30:35] 462 | 463 | **Colin**: For testing? 464 | 465 | **Speaker 25**: Yes. 466 | 467 | **Colin**: Testing what? Testing React in general? 468 | 469 | **Speaker 25**: [inaudible 01:30:45] 470 | 471 | **Colin**: Testing with styles. What do you want to do? 472 | 473 | **Speaker 25**: [inaudible 01:30:49] 474 | 475 | **Colin**: Testing CSS right now is hell, can we think of strategies for testing CSS now? Yeah, I guess we could, couldn't we? I hadn't thought a whole ton about that before you mentioned that but as you mention that I suppose that you could do all sorts of things. You could validate a value what's above or below a certain amount. You could check properties of objects. I imagine that could valuable. Especially if you're themeing and swapping things out or if something computes a value that's higher than it should be you could test to make sure that wasn't the case. I'm thinking about checks in the application itself as well. Like logical checks to make sure that something isn't above a certain width. You could really have any types of logic to check the style that you wanted. 476 | 477 | Others? 478 | 479 | Okay I'm going to give you maybe like five minutes just to play and build something crazy if you can think of it. Then send all of {% include skipper.html time="01:32:00" %} the crazy things that you built to me on Gitter and in the last five minutes we'll go through some things if build. Like animations on letters and individual things. 480 | 481 | If there's a really good one we'll put it on our blog. If you're going now, thank you, have a good night. Before we do that since people are going to start to go, thank you. 482 | 483 | Thank you guys, thank you for being here for three years. That's a lot of community time, that a lot of learning and it's a lot of time together and it's really exciting to have a community that's willing to do that and I really, really had a great experience for me to have all of your questions it's improved my own learning of React to prepare for these and also the edge space that I've had to consider because of all the apps you're building. Thank you. Ten more minutes and build something cool and we'll look at what other people built in the last few minutes. 484 | --------------------------------------------------------------------------------