├── readme.md ├── solution-code ├── .gitignore ├── .gitkeep ├── app │ ├── components │ │ ├── comment.jsx │ │ ├── mood-tracker.jsx │ │ ├── post.jsx │ │ └── profile-pic.jsx │ ├── data │ │ └── myPost.jsx │ └── index.jsx ├── dist │ └── bundle.js ├── gulpfile.js ├── index.html ├── package.json ├── readme.md └── server.js └── starter-code ├── .gitignore ├── .gitkeep ├── app ├── components │ └── hello-world.jsx └── index.jsx ├── dist └── bundle.js ├── gulpfile.js ├── index.html ├── package.json ├── readme.md └── server.js /readme.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ![](https://ga-dash.s3.amazonaws.com/production/assets/logo-9f88ae6c9c3871690e33280fcf557f33.png) 7 | 8 | #ReactJS 9 | 10 | ### Why is this important? 11 | 12 | *This workshop is important because:* 13 | 14 | React is a library that Facebook invented to help build custom HTML elements. React provides a declarative library that keeps the DOM (the view) in sync with your data (the model). It is concerned only with the **V** in **MVC** and as a result can be used in conjunction with other libraries to help manage state. 15 | 16 | ### What are the objectives? 17 | 18 | *After this workshop, developers will be able to:* 19 | 20 | * Create and render React components in the browser 21 | * Nest & embed React components 22 | * Modify the state of a React component through events 23 | 24 | ### Where should we be now? 25 | 26 | *Before this workshop, developers should already be able to:* 27 | 28 | * Write client-side applications in JavaScript 29 | * Knowledge of Gulp as a build tool 30 | 31 | ##From Docs 32 | 33 | >We built React to solve one problem: **building large applications with data that changes over time**. 34 | 35 | >Many people choose to think of React as the V in MVC. 36 | 37 | >In fact, with React the only thing you do is build components. Since they're so encapsulated, components make code reuse, testing, and separation of concerns easy. 38 | 39 | ##JSX 40 | 41 | JSX is a JavaScript syntax extension that looks similar to XML that helps represent DOM elements in JavaScript. 42 | 43 | ```js 44 | var myElement = ; 45 | ``` 46 | 47 | Here's an example of JSX, don't worry about understanding it just yet. 48 | 49 | ```js 50 | var Nav, Profile; 51 | // Input (JSX): 52 | var app = ; 53 | // Output (JS): 54 | var app = React.createElement( 55 | Nav, 56 | {color:"blue"}, 57 | React.createElement(Profile, null, "click") 58 | ); 59 | ``` 60 | 61 | ## Setup 62 | 63 | ###JSX Syntax in Sublime 64 | 65 | To view JSX appropriately in Sublime: 66 | 67 | * Install the package `babel` in sublime 68 | * Select syntax `Babel > Javascript (Babel)` 69 | 70 | ###Getting Started 71 | 72 | `cd` into `starter-code` 73 | 74 | * Contains a simple Express server that we'll use in today's class. 75 | * Look at the `readme.md` file at the root of the application to see how to get it setup. 76 | 77 | ## React Components 78 | 79 | ### Hello World: A Very Basic Component 80 | 81 | The basic unit you'll be working with in ReactJS is a **component**. 82 | 83 | * Using "components" is a pretty different way of approaching web development. 84 | 85 | It is common to separate HTML, CSS and Javascript from one another. 86 | 87 | * With components, there is more integration and less separation of HTML CSS and JS. 88 | * Instead, the pattern is to organize a web app into small, reusable components that encompass their own content, presentation and behavior. 89 | 90 | In `app/components/hello-world.jsx` let's create a super simple component that just says, "Hello World". In order to do this we're going to write in ES6 as it makes the syntax easier (and yes, we are consciously not using semicolons). 91 | 92 | ```js 93 | class HelloWorld extends React.Component { 94 | render() { 95 | return

Hello World!

96 | } 97 | } 98 | ``` 99 | From above, we can see that we are create a new Component class that is inheriting from `React.Component`. It has one function `render`. React expects `render` to be defined as that is the function that will get called when it is being rendered to the DOM. Note we are using ES6's [method definition syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#Description). 100 | 101 | Don't forget to require `React` and export your new component! 102 | 103 | ```js 104 | import React from 'react' 105 | 106 | class HelloWorld extends React.Component { 107 | render() { 108 | return

Hello World!

109 | } 110 | } 111 | export default HelloWorld 112 | ``` 113 | 114 | It could be refactored to [export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) the new class in the same line it is being defined. 115 | 116 | ```js 117 | import React from "react" 118 | 119 | export default class HelloWorld extends React.Component { 120 | render() { 121 | return

Hello World!

122 | } 123 | } 124 | ``` 125 | 126 | ###The Virtual DOM 127 | 128 | * Every component has, at minimum, a render method that generates a Virtual DOM node to be added to the actual DOM. 129 | * A Virtual DOM is just like a regular ol' DOM node, but it's not yet attached to the DOM. Try typing `var photo = new Image` in the console; that `photo` is part of the virtual DOM. 130 | * For React, the virtual DOM staging area for changes that will eventually be implemented. 131 | * The contents of this node are defined in the method's return statement using JSX. 132 | 133 | So we've created the template for our component. But how do we actually render it? 134 | 135 | The below `.render` function takes two arguments: 136 | 137 | * The component to render 138 | * The DOM element to append it to 139 | 140 | ```js 141 | ReactDOM.render( 142 | , 143 | document.getElementById("hello-world-component") 144 | ); 145 | ``` 146 | 147 | In your `app/index.jsx` let's import the new component and render it to the DOM. `React` is used for creating web components, while `ReactDOM` (used above) is used for actually rendering these elements to the DOM. Let's import our component and `ReactDOM` to get this working. It may look something like this: 148 | 149 | ```js 150 | "use strict" 151 | 152 | import React from 'react' 153 | import ReactDOM from 'react-dom' 154 | import HelloWorld from './components/hello-world' 155 | 156 | ReactDOM.render( 157 | , 158 | document.getElementById("hello-world-component") 159 | ) 160 | ``` 161 | 162 | >Note: It's a good idea to use [`"use strict"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) as it is a restricted variant of JavaScript that eliminates some of JavaScript's silent errors by changing them to throw errors. 163 | 164 | ### Mad Props 165 | 166 | Our `Hello` component isn't too helpful. Let's make it more interesting. 167 | 168 | * Rather than simply display "Hello world", let's display a greeting to the user. 169 | * How do we feed a name to our `Hello` component without hardcoding it into our render method? 170 | 171 | ```js 172 | // update your HelloWorld Component to accept a name prop 173 | export default class HelloWorld extends React.Component { 174 | render() { 175 | return

Hello {this.props.name}!

176 | } 177 | } 178 | ``` 179 | 180 | ```js 181 | // render it by passing in a `name` prop 182 | ReactDOM.render( 183 | , 184 | document.getElementById("hello-world-component") 185 | ) 186 | ``` 187 | 188 | What are `.props`? 189 | 190 | * Properties! Every React component has a `.props` method. 191 | * You can think of them like custom html attributes for React. 192 | * Properties are immutable and cannot be changed by the element itself, only by a parent. 193 | * We define properties and pass them in as attributes to the JSX element in our `.render` method. 194 | 195 | We can create multiple properties for a component. 196 | 197 | ```js 198 | export default class HelloWorld extends React.Component { 199 | render() { 200 | return ( 201 |
202 |

Hello {this.props.name}

203 |

You are {this.props.mood}!

204 |
205 | ) 206 | } 207 | } 208 | ``` 209 | 210 | >Note, if you have nested html elements in your JSX you have to wrap them in parenthesis. 211 | 212 | ```js 213 | ReactDOM.render( 214 | , 215 | document.getElementById("hello-world-component") 216 | ) 217 | ``` 218 | 219 | ### Challenge: Greet the day! 220 | 221 | Create a component called `` that has two props: `timeOfDay` and `object`, such that it's interface would be `` and it would print out "Goodnight moon" in an `h3`. Append it to a `div` with the `id` `greeting-component`. 222 | 223 |
224 | Example solution 225 | 226 | ```js 227 | class Greeting extends React.Component { 228 | render() { 229 | return( 230 |

Good{this.props.timeOfDay} {this.props.object}

231 | ) 232 | } 233 | } 234 | ``` 235 | 236 | ```js 237 | ReactDOM.render( 238 | , 239 | document.getElementById("greeting-component") 240 | ) 241 | ``` 242 | 243 |
244 | 245 | ###Variable Props 246 | 247 | What if we don't know exactly what props we're going to get but we want to pass them to the element. We can do this with something new in ES6 called a [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). 248 | 249 | For example lets say we to create `ProfilePic` component that is a clickable image tag and we want to be able to pass in any new props into it. 250 | 251 | ```js 252 | class ProfilePic extends React.Component { 253 | render() { 254 | return ( 255 | 256 | 257 | 258 | ) 259 | } 260 | } 261 | ``` 262 | 263 | Where we see `{...this.props}` is where all props we pass in will live. So now we can set an `href` and `class` on our element by just passing them in! 264 | 265 | >Note because `class` is a reserved word in ES6 we have to use `className` instead. 266 | 267 | ```js 268 | ReactDOM.render( 269 | , 270 | document.getElementById("profile-pic-component") 271 | ) 272 | ``` 273 | 274 | ###Nested Components 275 | 276 | What if we want to add text just above our Image, but want it also to be wrapped in the anchor tag so it is clickable, like the image? 277 | 278 | Well could we just put in some text inside of our `ProfilePic` component like such? You can nest HTML after all! 279 | 280 | ```js 281 | ReactDOM.render( 282 | 283 |

This Kitten Cashes Checks

284 |
, 285 | document.getElementById("profile-pic-component") 286 | ) 287 | ``` 288 | 289 | Yes, we could do this! We would just need to modify our original component to render any `children` we pass in the correct place, which is done with `this.props.children`. 290 | 291 | ```js 292 | class ProfilePic extends React.Component { 293 | render() { 294 | return ( 295 | 296 | {this.props.children} 297 | 298 | 299 | ) 300 | } 301 | } 302 | 303 | ``` 304 | 305 | ###Challenge: Mr Cat's Profile 306 | 307 | * Pass an `id` into the `ProfilePic` component that is `"mr-cat"` 308 | * Pass a `p` child, below the `h3`, into the `ProfilePic` component with the class `"bio"` that contains a brief description of this kitty. 309 | 310 | ##Exercise: A Blog Post 311 | 312 | Let's practice what we've learned so far by building a `Post` component for our blog. 313 | 314 | Make and render a custom `` component with the attributes `title`, `author`, and `body`. The exact HTML/CSS composition of the component is up to you. 315 | 316 |
317 | Example solution 318 | 319 | ```js 320 | class Post extends React.Component { 321 | render() { 322 | return ( 323 |
324 |

Hello {this.props.title}

325 |

By {this.props.author}

326 |

{this.props.body}

327 |
328 | ) 329 | } 330 | } 331 | ``` 332 | 333 | ```js 334 | ReactDOM.render( 335 | , 340 | document.getElementById("blog-component") 341 | ) 342 | ``` 343 | 344 |
345 | 346 | What if we wanted to add an array of comments to the post? 347 | 348 | ```js 349 | ReactDOM.render( 350 | _<"]} 355 | />, 356 | document.getElementById("blog-component") 357 | ) 358 | ``` 359 | 360 |
361 | Let's try map! 362 | 363 | ```js 364 | class Post extends React.Component { 365 | render() { 366 | return ( 367 |
368 |

{this.props.title}

369 |

By {this.props.author}

370 |

{this.props.body}

371 |

Comments

372 |
    { 373 | this.props.comments.map((comment, index) => { 374 | return
  • {comment}
  • 375 | }) 376 | }
377 |
378 | ) 379 | } 380 | } 381 | ``` 382 | 383 | >Note: when iterating through a collection, React needs to put a `key` on each item with a unique index for that collection (otherwise an errors will appear). `.map`'s second argument to it's callback is the (unique) index, so that works just fine. However, the actual unique database id on each comment would be better. 384 | 385 | ```js 386 | ReactDOM.render( 387 | , 393 | document.getElementById("blog-component") 394 | ) 395 | ``` 396 | 397 |
398 | 399 | ### Embedded Components 400 | 401 | * What if there was a comment component we could abstract some of this logic to? 402 | 403 | * Let's reference a comment using an embedded `` component inside of PostView's render method. 404 | 405 | ### Challenge: Add Embedded Comments To Blog 406 | 407 | 1. Create a `Comment` component in the same way we did for `Post`. 408 | * Your `Comment` render method should render a `body` property. 409 | 410 | 2. Amend your `Post`'s render method so that its return value generates as many `` elements as there are comments. 411 | * Make sure to pass in the body as an argument to the component. 412 | 413 |
414 | Example solution 415 | 416 | ```js 417 | class Post extends React.Component { 418 | render() { 419 | return ( 420 |
421 |

{this.props.title}

422 |

By {this.props.author}

423 |

{this.props.body}

424 |

Comments

425 |
    { 426 | this.props.comments.map((comment) => { 427 | return 428 | }) 429 | }
430 |
431 | ) 432 | } 433 | } 434 | 435 | class Comment extends React.Component { 436 | render() { 437 | return ( 438 |
  • {this.props.body}
  • 439 | ) 440 | } 441 | } 442 | ``` 443 |
    444 | 445 | ## State 446 | 447 | We already went over properties. 448 | 449 | * The thing about props is that they can only be changed by a parent. 450 | * So, what do we do if our component needs to tigger an update it's parent or the application as a whole? That's where **state** comes in. 451 | * State is similar to props, but is *meant to be changed*. 452 | * Like properties, we can access state values using `this.state.val`. 453 | * State is good when the applications needs to "respond to user input, a server request, or the passage of time" (all events). 454 | * Setting up and modifying state is not as straightforward as properties and instead requires multiple methods. 455 | * More on what [should & shouldn't go in state](http://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#what-should-go-in-state). 456 | 457 | Let's modify our earlier `HelloWorld` example to be a new `MoodTracker` component. There will be a mood displayed and eventually a user will click a button to indicate on a scale of 1-10 how much of that mood they are feeling. 458 | 459 | Now, let's instead use React's `.createClass` method as opposed to extending standard ES6 classes, as it makes some of this behavior more straightforward. 460 | 461 | ```js 462 | const MoodTracker = React.createClass({ 463 | getInitialState() { 464 | return {points: 1} 465 | }, 466 | render() { 467 | return ( 468 |
    469 |

    Hello {this.props.name}

    470 |

    On a scale of 1-10

    471 |

    You are {this.props.mood}

    472 |

    This much: {this.state.points}

    473 |
    474 | ) 475 | } 476 | }) 477 | ``` 478 | 479 | Next we need to enable the user to change the state of our component. Let's create an `onClick` event that triggers a method `increaseMood` to increment our counter by 1 for each click. Notice that it is important to use the [`.setState`](https://facebook.github.io/react/docs/component-api.html#setstate) method to update the state. Also, we can define the initial state with `getInitialState` a reserved method in React. 480 | 481 | ```js 482 | const MoodTracker = React.createClass({ 483 | getInitialState() { 484 | return {points: 1} 485 | }, 486 | increaseMood() { 487 | this.setState({ 488 | points: this.state.points + 1 489 | }) 490 | }, 491 | render() { 492 | return ( 493 |
    494 |

    Hello {this.props.name}

    495 |

    On a scale of 1-10

    496 |

    You are {this.props.mood}

    497 |

    This much: {this.state.points}

    498 | 499 |
    500 | ) 501 | } 502 | }) 503 | ``` 504 | 505 | Whenever we run `.setState`, our component runs a **diff** between the current DOM and the virtual DOM node to update the state of the DOM in as few manipulations as possible. 506 | 507 | * Only replaces the current DOM with parts that have changed. 508 | * This is super important! Using React, we only change parts of the DOM that need to be changed. 509 | * Implications on performance. 510 | * We **do not** re-render the entire component like we have been in class. 511 | * This is one of React's core advantages 512 | 513 | ### Challenge: Count to 10 514 | 515 | After 10 clicks, the user should see the counter reset to 1. 516 | 517 |
    518 | Example solution 519 | 520 | ```js 521 | const MoodTracker = React.createClass({ 522 | getInitialState() { 523 | return {points: 1} 524 | }, 525 | increaseMood() { 526 | let newPoints = this.state.points >= 10 ? 1 : this.state.points + 1 527 | this.setState({ 528 | points: newPoints 529 | }) 530 | }, 531 | render() { 532 | return ( 533 |
    534 |

    Hello {this.props.name}

    535 |

    On a scale of 1-10

    536 |

    You are {this.props.mood}

    537 |

    This much: {this.state.points}

    538 | 539 |
    540 | ) 541 | } 542 | }) 543 | ``` 544 | 545 |
    546 | 547 | ## Challenge: I Like! 548 | 549 | Let's create a state for our earlier blog example. We want to be able to edit the body of our post. Tip: update the component to the `React.createClass` syntax. 550 | 551 | 1. Initialize a `likes` state for our `Post` component that defaults to 0. 552 | 1. Render the number of likes somewhere in your component. 553 | 1. Create a method, `like` that adds 1 to the state's number of `likes`. 554 | 1. Create a button that triggers the above function. 555 | 556 | ### Solution 557 | 558 | Checkout the `solution-code` directory. 559 | 560 | ## What's Next? 561 | 562 | * Getting user input with [forms](https://facebook.github.io/react/docs/forms.html) 563 | * Triggering [events](https://facebook.github.io/react/tips/dom-event-listeners.html) 564 | 565 | ## Questions / Closing 566 | 567 |
    568 | Having learned the basics of React, what are some benefits to using it vs. a different framework or plain ol' Javascript? 569 | 570 | * Reusable components. 571 | * High level of control. 572 | * Imperative components, declarative views. 573 | * Convenient templating of data. 574 | * Fast rendering. 575 | 576 |
    577 | -------------------------------------------------------------------------------- /solution-code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /solution-code/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sf-wdi-gaia/react/023d5c534156103c2724bc21a70e72056120d2d2/solution-code/.gitkeep -------------------------------------------------------------------------------- /solution-code/app/components/comment.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class Comment extends React.Component { 4 | render() { 5 | return ( 6 |
  • {this.props.body}
  • 7 | ) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /solution-code/app/components/mood-tracker.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export default React.createClass({ 4 | getInitialState() { 5 | return {points: 1} 6 | }, 7 | 8 | increaseMood() { 9 | let newPoints = this.state.points >= 10 ? 1 : this.state.points + 1 10 | this.setState({ 11 | points: newPoints 12 | }) 13 | }, 14 | render() { 15 | return ( 16 |
    17 |

    Hello {this.props.name}

    18 |

    On a scale of 1-10

    19 |

    You are {this.props.mood}

    20 |

    This much: {this.state.points}

    21 | 22 |
    23 | ) 24 | } 25 | }) -------------------------------------------------------------------------------- /solution-code/app/components/post.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Comment from './comment' 3 | 4 | export default React.createClass({ 5 | 6 | getInitialState() { 7 | return {likes: 0} 8 | }, 9 | 10 | like() { 11 | this.setState({ 12 | likes: ++this.state.likes 13 | }) 14 | }, 15 | 16 | render() { 17 | return ( 18 |
    19 |

    {this.props.title}

    20 |

    By {this.props.author}

    21 |

    {this.props.body}

    22 |

    Likes: {this.state.likes}

    23 | 24 |

    Comments

    25 |
      { 26 | this.props.comments.map((comment, index) => { 27 | return 28 | }) 29 | }
    30 |
    31 | ) 32 | } 33 | }) -------------------------------------------------------------------------------- /solution-code/app/components/profile-pic.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class ProfilePic extends React.Component { 4 | render() { 5 | return ( 6 | 7 | {this.props.children} 8 | 9 | 10 | ) 11 | } 12 | } -------------------------------------------------------------------------------- /solution-code/app/data/myPost.jsx: -------------------------------------------------------------------------------- 1 | // Create Post object 2 | export default { 3 | title: "My First Post", 4 | author: "Mr Cat", 5 | body: "Check it out. This is the first post on my dope blog!", 6 | comments: ["First!", "Second!", "This blog needs more GIFs."] 7 | }; -------------------------------------------------------------------------------- /solution-code/app/index.jsx: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | import React from 'react' 4 | import ReactDOM from 'react-dom' 5 | import Post from './components/post' 6 | import ProfilePic from './components/profile-pic' 7 | import myPostData from './data/myPost' 8 | 9 | ReactDOM.render( 10 | , 16 | document.getElementById("blog-component") 17 | ) 18 | 19 | ReactDOM.render( 20 | 21 |

    This Kitten Cashes Checks

    22 |

    The coolest cat around town

    23 |
    , 24 | document.getElementById("profile-pic-component") 25 | ) -------------------------------------------------------------------------------- /solution-code/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserify = require('browserify'); 3 | var babelify = require('babelify'); 4 | var source = require('vinyl-source-stream'); 5 | 6 | gulp.task('build', function () { 7 | browserify({ 8 | entries: 'app/index.jsx', 9 | extensions: ['.jsx'], 10 | debug: true 11 | }) 12 | .transform(babelify, {presets: ['es2015', 'react']}) 13 | .on('error', function (err) { 14 | console.log(err); 15 | this.emit('end'); 16 | }) 17 | .bundle() 18 | .on('error', function(err){ 19 | console.log(err.message); 20 | }) 21 | .pipe(source('bundle.js')) 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | 25 | gulp.task('watch', ['build'], function () { 26 | console.log("watching for changes..."); 27 | gulp.watch('app/**/*.jsx', ['build']); 28 | }); 29 | 30 | gulp.task('default', ['watch']); 31 | -------------------------------------------------------------------------------- /solution-code/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /solution-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "postinstall": "gulp", 8 | "start": "nodemon" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babelify": "^7.2.0", 14 | "browserify": "^13.0.0", 15 | "gulp": "^3.9.1", 16 | "vinyl-source-stream": "^1.1.0" 17 | }, 18 | "dependencies": { 19 | "babel-preset-es2015": "^6.6.0", 20 | "babel-preset-react": "^6.5.0", 21 | "express": "^4.13.4", 22 | "react": "^0.14.7", 23 | "react-dom": "^0.14.7" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solution-code/readme.md: -------------------------------------------------------------------------------- 1 | #Setup 2 | 3 | * Run `npm install` to install dependencies and start your `gulp` task (which can be run seperately at any time with just `gulp`) 4 | * Run `npm start` to start a `nodemon` server on `localhost:3000` -------------------------------------------------------------------------------- /solution-code/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | app = express(), 3 | path = require('path'); 4 | 5 | 6 | app.set('port', process.env.PORT || 3000) 7 | 8 | app.use(express.static(__dirname + '/dist')); 9 | 10 | app.get('/', function (req, res) { 11 | res.sendFile(path.join(__dirname, "index.html")); 12 | }); 13 | 14 | app.listen(app.get('port'), function () { 15 | console.log('Listening on port', app.get('port')); 16 | }); -------------------------------------------------------------------------------- /starter-code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /starter-code/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sf-wdi-gaia/react/023d5c534156103c2724bc21a70e72056120d2d2/starter-code/.gitkeep -------------------------------------------------------------------------------- /starter-code/app/components/hello-world.jsx: -------------------------------------------------------------------------------- 1 | // hello world component... 2 | -------------------------------------------------------------------------------- /starter-code/app/index.jsx: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | import React from 'react' 4 | import ReactDOM from 'react-dom' 5 | import HelloWorld from './components/hello-world' 6 | 7 | 8 | ReactDOM.render({}) 9 | -------------------------------------------------------------------------------- /starter-code/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserify = require('browserify'); 3 | var babelify = require('babelify'); 4 | var source = require('vinyl-source-stream'); 5 | 6 | gulp.task('build', function () { 7 | browserify({ 8 | entries: 'app/index.jsx', 9 | extensions: ['.jsx'], 10 | debug: true 11 | }) 12 | .transform(babelify, {presets: ['es2015', 'react']}) 13 | .bundle() 14 | .on('error', function(err){ 15 | console.log(err.message); 16 | }) 17 | .pipe(source('bundle.js')) 18 | .pipe(gulp.dest('dist')); 19 | }); 20 | 21 | gulp.task('watch', ['build'], function () { 22 | console.log("watching for changes..."); 23 | gulp.watch('app/**/*.jsx', ['build']); 24 | }); 25 | 26 | gulp.task('default', ['watch']); 27 | -------------------------------------------------------------------------------- /starter-code/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React 6 | 7 | 8 | 9 |

    Make React!

    10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /starter-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "postinstall": "gulp", 8 | "start": "nodemon" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babelify": "^7.2.0", 14 | "browserify": "^13.0.0", 15 | "gulp": "^3.9.1", 16 | "vinyl-source-stream": "^1.1.0" 17 | }, 18 | "dependencies": { 19 | "babel-preset-es2015": "^6.6.0", 20 | "babel-preset-react": "^6.5.0", 21 | "express": "^4.13.4", 22 | "react": "^0.14.7", 23 | "react-dom": "^0.14.7" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /starter-code/readme.md: -------------------------------------------------------------------------------- 1 | #Setup 2 | 3 | * Run `npm install` to install dependencies and start your `gulp` task (which can be run seperately at any time with just `gulp`) 4 | * Run `npm start` to start a `nodemon` server on `localhost:3000` -------------------------------------------------------------------------------- /starter-code/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | app = express(), 3 | path = require('path'); 4 | 5 | 6 | app.set('port', process.env.PORT || 3000) 7 | 8 | app.use(express.static(__dirname + '/dist')); 9 | 10 | app.get('/', function (req, res) { 11 | res.sendFile(path.join(__dirname, "index.html")); 12 | }); 13 | 14 | app.listen(app.get('port'), function () { 15 | console.log('Listening on port', app.get('port')); 16 | }); --------------------------------------------------------------------------------