9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/exercise01/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | /**
5 | * Everything in React starts with a component. A component can be created by
6 | * using `React.createElement`. The result of this can then be rendered using
7 | * `ReactDOM.render`.
8 | *
9 | * Example:
10 | *
11 | * ```
12 | * ReactDOM.render(
13 | * React.createElement('div', null, 'This is my component'),
14 | * document.getElementById('container')
15 | * );
16 | * ```
17 | *
18 | * This is similar to the following code in JavaScript:
19 | *
20 | * ```
21 | * let component = document.createElement('div');
22 | * let container = document.getElementById('container');
23 | *
24 | * component.innerHTML = 'This is my component';
25 | * container.innerHTML = '';
26 | *
27 | * container.appendChild(component);
28 | * ```
29 | *
30 | * API documentation:
31 | *
32 | * `React.createElement` creates a `ReactElement` and takes the following arguments:
33 | * - type: This can be an HTML tag name (e.g., 'div', 'span', etc), or a `ReactClass`
34 | * - props: This is an object defining the properties for the element
35 | * - children: This is the inner content of the element, which can be a string of text, or `ReactElement`
36 | *
37 | * `ReactDOM.render` will render an element to the DOM and takes two arguments:
38 | * - element: This is the `ReactElement` to be rendered.
39 | * - container: This is the DOM element to render to.
40 | *
41 | * Exercise:
42 | *
43 | * Create a `ReactElement` that uses a `div` as it's element and renders the
44 | * text "Hello World".
45 | */
46 |
47 | ReactDOM.render(
48 | React.createElement('div', null, 'Hello World'),
49 | document.getElementById('container')
50 | );
51 |
52 | /**
53 | * React offers a markup extension to make building declarative UIs more
54 | * familiar to those of us coming from an HTML background. This extension is
55 | * called JSX. This is completely optional when using React and requires a
56 | * transpiler like babel + babel-preset-react to run in the browser. This is
57 | * an acceptable trade off as you are likely already using babel +
58 | * babel-preset-es2015 to transpile your ES6 syntax to work in older browsers.
59 | *
60 | * Let's look at an example of JSX:
61 | *
62 | * ```
63 | * ReactDOM.render(
This is my component
, document.getElementById('container'));
64 | * ```
65 | *
66 | * I know what you're thinking: "Why is there HTML in my JavaScript?!". This is
67 | * a typical initial reaction. Remember it's optional to use JSX, though once
68 | * you get past the initial gag reflex, it will grow on you and you'll enjoy
69 | * the flavor :).
70 | *
71 | * It's important to note that this produces exactly what you see in the first
72 | * example once it has been transpiled.
73 | *
74 | * Exercise:
75 | *
76 | * Refactor your previous solution to use JSX.
77 | */
78 |
79 | ReactDOM.render(
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/exercise02/index.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | const container = document.getElementById('container');
5 |
6 | /**
7 | * React components accept properties which allow us to pass data into a
8 | * component from the outside. This is very similar to using attributes
9 | * with native HTML elements.
10 | *
11 | * Example:
12 | *
13 | * ```
14 | * ReactDOM.render(
15 | *
16 | * document.getElementById('container')
17 | * );
18 | * ```
19 | *
20 | * By passing in properties, the component can be used to hide away render
21 | * logic that may otherwise be repeated over and over, but still allows for
22 | * passing in information unique to a given instance. This is exactly the same
23 | * as declaring a function to calculate how old a person is, for example,and
24 | * passing in their birth date as an argument.
25 | *
26 | * In order to use props we have to define our component a little differently
27 | * than we have done in the previous example.
28 | *
29 | * ```
30 | * let Hello = (props) => {
31 | * return
Hello {props.children}
;
32 | * };
33 | *
34 | * ReactDOM.render(World, container);
35 | * ```
36 | *
37 | * This example uses a reserved prop name `children` which will render whatever
38 | * is passed in as the body of the component. This is similar to using
39 | * "transclude" with an Angular directive.
40 | *
41 | * You also likely noticed the curly braces around `props.children`. This tells
42 | * JSX to evaluate what's between the braces as literal JavaScript.
43 | *
44 | * You may also accept any props you need for your component to render.
45 | *
46 | * ```
47 | * let OrderDetail = (props) => {
48 | * return (
49 | *
50 | *
Order Number: {props.order.number}
51 | *
Quantity: {props.order.quantity
52 | *
Order Total: {props.order.quantity * props.product.price}
53 | *
Product: {props.product.name}
54 | *
55 | *
56 | * );
57 | * };
58 | *
59 | * ReactDOM.render(
60 | * , container
71 | * );
72 | * ```
73 | *
74 | * Exercise:
75 | *
76 | * Create a component that takes a user's first and last name and renders
77 | * "Hello Jane Doe!" (assuming first name is Jane and last name is Doe).
78 | */
79 |
80 | let SayHello = (props) => {
81 | return (
82 |
Hello {props.firstName} {props.lastName}!
83 | );
84 | };
85 |
86 | ReactDOM.render(, container);
87 |
88 | /**
89 | * Often you will want to validate that properties that are provided to your
90 | * component. This could be anything from verifying the data type of a prop,
91 | * or could be indicating that a prop is required. React provides a mechanism
92 | * for specifying the validation of props as well as providing default values
93 | * in the event that a prop isn't required and no value is provided. This is
94 | * done using `propTypes` and `defaultProps`.
95 | *
96 | * Example:
97 | *
98 | * ```
99 | * let OrderDetail = (props) => {
100 | * return (
101 | *
102 | *
Order Number: {props.order.number}
103 | *
Quantity: {props.order.quantity
104 | *
Order Total: {props.order.quantity * props.product.price}
105 | *
Product: {props.product.name}
106 | *
107 | *
108 | * );
109 | * };
110 | *
111 | * OrderDetail.defaultProps = {
112 | * order: {},
113 | * product: {}
114 | * };
115 | *
116 | * OrderDetail.propTypes = {
117 | * order: PropTypes.object,
118 | * product: PropTypes.object
119 | * };
120 | *
121 | * ReactDom.render(, container);
122 | * ```
123 | *
124 | * See https://facebook.github.io/react/docs/reusable-components.html#prop-validation
125 | *
126 | * Exercise:
127 | *
128 | * Create a component that takes a user object as a prop, provides a default
129 | * value, validates that the prop is an object, and renders
130 | * "Hello firstName lastName!".
131 | */
132 |
133 | let ValidateHello = (props) => {
134 | return (
135 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/exercises/exercise03/index.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | const container = document.getElementById('container');
5 |
6 | /**
7 | * React components are styled very similarly to how you would natively style
8 | * an HTML element. You have two options: CSS classes and inline styles. These
9 | * styles are passed into a component by using props as we just learned about.
10 | *
11 | * Example:
12 | *
13 | * ```
14 | * ReactDOM.render(, container);
15 | * ```
16 | *
17 | * You may have noticed that the prop name is `className` as opposed to `class`.
18 | * This is to avoid conflicts with the reserved word `class` in JavaScript.
19 | * Using the `className` property will apply the given CSS class to the element.
20 | *
21 | * As mentioned you can also use inline styles. This approach allows you to set
22 | * styles directly to an element without using CSS classes, but rather by
23 | * defining them as an object.
24 | *
25 | * Example:
26 | *
27 | * ```
28 | * ReactDOM.render(
29 | * , container
34 | * );
35 | * ```
36 | *
37 | * When using inline styles CSS property names that contain a hyphen shohuld be
38 | * converted to camel case (e.g., font-size -> fontSize). Also by default any
39 | * property that has a numeric value will be assumed to have a unit of `px`.
40 | *
41 | * Exercise:
42 | *
43 | * Create a `Box` component that renders a `div` and accepts a `size` and
44 | * `style` property. Some CSS classes are provided:
45 | *
46 | * - Box: provides basic styling for the component
47 | * - Box--large: renders a large box
48 | * - Box--medium: renders a medium box
49 | * - Box--small: renders a small box
50 | *
51 | * If the `size` is `large` the component should use the class `Box--large`.
52 | * It should also use whatever inline styles are provided.
53 | */
54 |
55 | let Box = (props) => {
56 | return (
57 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/exercises/exercise04/index.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | const container = document.getElementById('container');
5 |
6 | /**
7 | * As mentioned at the begining, everything in React starts with a component.
8 | * Using components as the building block, we can assemble multiple
9 | * components together to create more complex components, or apps.
10 | *
11 | * Up until now we have only created components that render native DOM elements
12 | * as their content. Once we have our own custom component however, it is
13 | * exactly the same to render that instead of a native element.
14 | *
15 | * Example:
16 | *
17 | * ```
18 | * let FormField = (props) => {
19 | * return (
20 | *
21 | *
22 | * {props.children}
23 | *
24 | * );
25 | * };
26 | *
27 | * let LoginForm = () => {
28 | * return (
29 | *
38 | * );
39 | * };
40 | *
41 | * ReactDOM.render(, container);
42 | * ```
43 | *
44 | * We could now potentially move `FormField` into it's own file which would
45 | * allow it to be tested in isolation as well as be imported anywhere else
46 | * in our application that it may be needed.
47 | *
48 | * Exercise:
49 | *
50 | * Create a `Person` component using the following spec.
51 | *
52 | * `Person`:
53 | * - Render a `div` with a `className` of `Person`
54 | * - Accept the props `name`, `title`, `avatar`, `twitter`, `github`
55 | * - Render the name and title of the person
56 | * - Render the `Avatar`
57 | * - Render links to twitter and github using `Icon`
58 | *
59 | * `Avatar`:
60 | * - Render a `img` with a `className` of `Avatar`
61 | * - Accept the props `size` and `url`
62 | * - Adjust the width/height of img according to size
63 | *
64 | * `Icon`:
65 | * - Render an `a` with a `className` of `Icon`
66 | * - Accept the props `href` and `type`
67 | * - Render a font-awesome icon based on `type`
68 | */
69 |
70 | let Person = (props) => {
71 | return (
72 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/exercises/exercise05/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | const container = document.getElementById('container');
5 |
6 | /**
7 | * Where props allow for data to be passed into a component from the outside,
8 | * state allows a component to keep track of it's own data needed for rendering.
9 | *
10 | * In order for a component to use state we need to use a more complex way of
11 | * creating a component.
12 | *
13 | * Example:
14 | *
15 | * ```
16 | * class ClickCounter extends Component {
17 | * constructor(props) {
18 | * super(props);
19 | *
20 | * this.state = {
21 | * clicks: 0
22 | * };
23 | *
24 | * this.handleButtonClick = this.handleButtonClick.bind(this);
25 | * }
26 | *
27 | * handleButtonClick() {
28 | * this.setState({
29 | * clicks: this.state.clicks + 1
30 | * });
31 | * }
32 | *
33 | * render() {
34 | * return (
35 | *
36 | *
37 | *
Clicked {this.state.clicks} times
38 | *
39 | * );
40 | * }
41 | * }
42 | * ```
43 | *
44 | * Calling `setState` will queue the internal state for the component to be
45 | * updated. Once it has changed `render` will be called and the component will
46 | * once again be rendered with the changes. Only this point of the render tree
47 | * down will be affected.
48 | *
49 | * There are a handful of lifecycle methods that come into play when dealing
50 | * with state, such as `componentWillUpdate`, `componentDidUpdate`, etc.
51 | *
52 | * See https://facebook.github.io/react/docs/component-specs.html
53 | *
54 | * Exercise:
55 | *
56 | * Create a `StopWatch` component that has a Start/Stop button and a Clear
57 | * button. Pressing Start will start a timer and the lapsed time in
58 | * milliseconds should be displayed above the buttons. Once started the
59 | * Start button should change to Stop. Clicking Stop will stop the timer
60 | * but lapsed time will be preserved. Clicking Start again will resume
61 | * the timer from where it left off. Clicking Clear will stop the timer
62 | * if it's running and reset the lapsed time to 0.
63 | */
64 |
65 | class StopWatch extends Component {
66 | constructor() {
67 | super(...arguments);
68 |
69 | this.state = {
70 | running: false,
71 | lapse: 0,
72 | now: 0
73 | };
74 |
75 | this.timer = null;
76 | this.handleRunClick = this.handleRunClick.bind(this);
77 | this.handleClearClick = this.handleClearClick.bind(this);
78 | }
79 |
80 | handleRunClick() {
81 | if (this.state.running) {
82 | this.stop();
83 | } else {
84 | this.start();
85 | }
86 | }
87 |
88 | handleClearClick() {
89 | this.stop();
90 | this.setState({
91 | lapse: 0,
92 | now: 0
93 | });
94 | }
95 |
96 | start() {
97 | this.timer = setInterval(() => {
98 | this.setState({
99 | lapse: Date.now() - this.state.now
100 | }, 1);
101 | });
102 |
103 | this.setState({
104 | running: true,
105 | now: Date.now() - this.state.lapse
106 | });
107 | }
108 |
109 | stop() {
110 | clearInterval(this.timer);
111 | this.timer = null;
112 |
113 | this.setState({
114 | running: false
115 | });
116 | }
117 |
118 | render() {
119 | return (
120 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/exercise06/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import axios from 'axios';
4 |
5 | const container = document.getElementById('container');
6 |
7 | /**
8 | * React does not provide a module for sending HTTP requests. To do this we
9 | * need to bring in a third-party library. For this exercies we will use a
10 | * libary called `axios`.
11 | *
12 | * It is best not to fetch data from a server in the `render` method. As we
13 | * saw in the last exercise any change to the state of a component can cause
14 | * a re-render of the component. This will likely happen more often than we
15 | * want. It is best to use another lifecycle method `componentWillMount` to
16 | * make these requests. This method will be called once before the component
17 | * is inserted into the document, regardless of how many times `render` is
18 | * called.
19 | *
20 | * Example:
21 | *
22 | * ```
23 | * class UserProfile extends Component {
24 | * constructor(props) {
25 | * super(props);
26 | *
27 | * this.state = {
28 | * user: {}
29 | * };
30 | * }
31 | *
32 | * componentWillMount() {
33 | * axios(`/users/${this.props.id}`)
34 | * .then((response) => {
35 | * this.setState({
36 | * user: response.data
37 | * });
38 | * });
39 | * }
40 | *
41 | * render() {
42 | * let user = this.state.user;
43 | * return (
44 | *
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/exercises/exercise07/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Router, Route, IndexRoute, Link, hashHistory, useRouterHistory } from 'react-router';
4 | import { createHashHistory } from 'history';
5 |
6 | const container = document.getElementById('container');
7 | const USERS = [
8 | {
9 | id: 1,
10 | firstName: 'Fred',
11 | lastName: 'Flintstone',
12 | avatar: 'https://upload.wikimedia.org/wikipedia/en/thumb/a/ad/Fred_Flintstone.png/165px-Fred_Flintstone.png'
13 | },
14 | {
15 | id: 2,
16 | firstName: 'Wilma',
17 | lastName: 'Flintstone',
18 | avatar: 'https://upload.wikimedia.org/wikipedia/en/9/97/Wilma_Flintstone.png'
19 | },
20 | {
21 | id: 3,
22 | firstName: 'Pebbles',
23 | lastName: 'Flintstone',
24 | avatar: 'https://upload.wikimedia.org/wikipedia/en/thumb/0/0a/Pebbles_Flintstone.png/155px-Pebbles_Flintstone.png'
25 | },
26 | {
27 | id: 4,
28 | firstName: 'Barney',
29 | lastName: 'Rubble',
30 | avatar: 'https://upload.wikimedia.org/wikipedia/en/thumb/e/e2/Barney_Rubble.png/160px-Barney_Rubble.png'
31 | },
32 | {
33 | id: 5,
34 | firstName: 'Betty',
35 | lastName: 'Rubble',
36 | avatar: 'https://upload.wikimedia.org/wikipedia/en/5/5e/Betty_Rubble.png'
37 | },
38 | {
39 | id: 6,
40 | firstName: 'Bamm-Bamm',
41 | lastName: 'Rubble',
42 | avatar: 'https://upload.wikimedia.org/wikipedia/en/thumb/0/0c/Bamm-Bamm_Rubble.png/180px-Bamm-Bamm_Rubble.png'
43 | }
44 | ];
45 |
46 | /**
47 | * Client side routing for single page apps with React is made possible with
48 | * third-party libraries like `react-router`. Using a client router like this
49 | * allows you to link to different parts of your app without reloading the page.
50 | * React router behaves similarly to server side libraries like `express` where
51 | * you define the path to a route and then specify how that route should be
52 | * handled.
53 | *
54 | * Example:
55 | *
56 | * ```
57 | * let Home = () => Home;
58 | * let ProductList = () => Product List;
59 | * let ProductDetail = (props) => Product Detail (props.routeParams.productId);
60 | *
61 | * let Routes = (
62 | *
63 | *
64 | *
65 | *
66 | *
67 | * );
68 | *
69 | * ReactDOM.render(Routes, component);
70 | * ```
71 | *
72 | * In this example we have three routes defined, a home route, a product list,
73 | * and a product detail. If the `path` prop matches the current location
74 | * then the `component` for that route will be rendered.
75 | *
76 | * You may also notice the product detail `path` contains `:productId`.
77 | * This is a named parameter portion of the path. If the location were
78 | * `/product/37` then the product detail route would be matched and rendered.
79 | *
80 | * You will also notice that the `ProductDetail` component is also given a
81 | * prop called `routeParams`. This is provided by `react-router` and contains
82 | * all the named parameters that are defined in the route's path.
83 | *
84 | * You can create links within your app to these routes using the `Link`
85 | * component provided by `react-router`.
86 | *
87 | * Example:
88 | *
89 | * ```
90 | * let ProductList = () => {
91 | * return (
92 | *
93 | *
Product 1
94 | *
Product 2
95 | *
Product 3
96 | *
Product 4
97 | *
98 | * );
99 | * };
100 | * ```
101 | *
102 | * Exercise:
103 | *
104 | * Create an app that uses `react-router`. The app should have a `/` route,
105 | * a `/users` route which lists all the `USERS`, and a `/user/:userId` route
106 | * that shows the details for the user matching `userId` parameter.
107 | */
108 |
109 | let App = (props) => {
110 | return (
111 |
112 |
113 |
Home
114 |
Users
115 |
116 |
{props.children}
117 |
118 | );
119 | }
120 |
121 | let Home = () => {
122 | return