19 |
20 | );
21 | };
22 |
23 | render(, document.querySelector('#root'));
24 |
--------------------------------------------------------------------------------
/finished/src/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 10px;
3 | }
4 |
5 | body {
6 | font-family: sans-serif;
7 | background: #ffc425;
8 | background: linear-gradient(to bottom, #ffc425 50%, #eaaa00 100%);
9 | background-attachment: fixed;
10 | }
11 |
12 | p {
13 | font-size: 1.8rem;
14 | line-height: 1.5;
15 | }
16 |
17 | a {
18 | color: #0d181c;
19 | }
20 |
21 | html {
22 | box-sizing: border-box;
23 | }
24 |
25 | *,
26 | *:before,
27 | *:after {
28 | box-sizing: inherit;
29 | }
30 |
31 | img {
32 | max-width: 100%;
33 | }
34 |
35 | h1 {
36 | text-align: center;
37 | font-family: 'Bevan', cursive;
38 | font-weight: normal;
39 | transform: rotate(-5deg) skew(-20deg);
40 | margin: 2rem 0 2rem 0;
41 | }
42 |
43 | h1 a {
44 | text-decoration: none;
45 | font-size: 10rem;
46 | text-shadow: 3px 3px 0 rgba(13,24,28,0.2);
47 | }
48 |
49 | .beer {
50 | background: #fff;
51 | padding: 1rem;
52 | border-top: 1.5rem solid #e13e45;
53 | flex: 1 0 auto;
54 | margin: 2rem;
55 | overflow: hidden;
56 | width: calc(33.3% - 4rem);
57 | box-shadow: 0 0 10px rgba(13,24,28,0.2);
58 | transition: all 0.2s;
59 | }
60 |
61 | .beer:hover {
62 | transform: scale(1.05);
63 | box-shadow: 0 0 20px 2px rgba(13,24,28,0.3);
64 | }
65 |
66 | .beer a {
67 | text-decoration: none;
68 | }
69 |
70 | .beer h2 {
71 | margin: 0;
72 | font-size: 1.5rem;
73 | text-align: center;
74 | margin-bottom: 2rem;
75 | }
76 |
77 | .beers {
78 | display: flex;
79 | flex-wrap: wrap;
80 | max-width: 900px;
81 | margin: 0 auto;
82 | }
83 | /*
84 | Search
85 | */
86 | .search {
87 | text-align: center;
88 | max-width: 500px;
89 | margin: 2rem auto;
90 | }
91 |
92 | .search form {
93 | display: flex;
94 | }
95 |
96 | .search form > * {
97 | flex: 1;
98 | background: #fff;
99 | border: 0;
100 | padding: 2rem;
101 | font-size: 2rem;
102 | }
103 |
104 | .search form > *[type="submit"] {
105 | flex-grow: 2;
106 | background: #e13e45;
107 | color: #fff;
108 | text-transform: uppercase;
109 | }
110 |
111 | .search form > *[type="text"] {
112 | flex-grow: 10;
113 | }
114 | /*
115 | Loader
116 | */
117 | .loader {
118 | margin: 0 auto;
119 | text-align: center;
120 | font-size: 3rem;
121 | font-weight: 100;
122 | }
123 | /*
124 | Single Beer View
125 | */
126 | .single-beer {
127 | max-width: 500px;
128 | margin: 0 auto;
129 | background: #fff;
130 | padding: 2rem;
131 | }
132 |
133 | .single-beer .label {
134 | width: 100%;
135 | }
136 |
137 | .single-beer img {
138 | max-width: 100%;
139 | }
140 |
141 | .single-beer h3 {
142 | font-size: 2rem;
143 | }
144 |
145 | .glass img {
146 | float: left;
147 | width: 200px;
148 | }
149 |
--------------------------------------------------------------------------------
/finished/src/style.styl:
--------------------------------------------------------------------------------
1 | yellow = #FFC425
2 | black = #0D181C
3 | red = #e13e45
4 | bevan = 'Bevan', cursive;
5 |
6 |
7 | html
8 | font-size 10px
9 |
10 | body
11 | font-family sans-serif
12 | background yellow
13 | background: linear-gradient(to bottom, yellow 50%,darken(yellow,20%) 100%);
14 | background-attachment fixed
15 |
16 | p
17 | font-size 1.8rem
18 | line-height 1.5
19 |
20 | a
21 | color black
22 |
23 | html
24 | box-sizing: border-box;
25 |
26 | *, *:before, *:after
27 | box-sizing: inherit;
28 |
29 | img
30 | max-width 100%
31 |
32 | h1
33 | text-align center
34 | font-family bevan
35 | font-weight normal
36 | transform rotate(-5deg) skew(-20deg)
37 | margin 2rem 0 2rem 0
38 | a
39 | text-decoration none
40 | font-size 10rem
41 | text-shadow 3px 3px 0 alpha(black, 20%)
42 |
43 | .beer
44 | background white
45 | padding 1rem
46 | border-top 1.5rem solid red
47 | flex 1 0 auto
48 | margin 2rem
49 | overflow hidden
50 | width calc(33.3% - 4rem)
51 | box-shadow 0 0 10px alpha(black, 20%)
52 | transition all 0.2s
53 | &:hover
54 | transform scale(1.05)
55 | box-shadow 0 0 20px 2px alpha(black, 30%)
56 | a
57 | text-decoration none
58 | h2
59 | margin 0
60 | font-size 1.5rem
61 | text-align center
62 | margin-bottom 2rem
63 |
64 | .beers
65 | display flex
66 | flex-wrap wrap
67 | max-width 900px
68 | margin 0 auto
69 |
70 |
71 | /*
72 | Search
73 | */
74 |
75 | .search
76 | text-align center
77 | max-width 500px
78 | margin 2rem auto
79 | form
80 | display flex
81 | & > *
82 | flex 1
83 | background white
84 | border 0
85 | padding 2rem
86 | font-size 2rem
87 | &[type="submit"]
88 | flex-grow 2
89 | background red
90 | color white
91 | text-transform uppercase
92 | &[type="text"]
93 | flex-grow 10
94 |
95 | /*
96 | Loader
97 | */
98 |
99 | .loader
100 | margin 0 auto
101 | text-align center
102 | font-size 3rem
103 | font-weight 100
104 |
105 |
106 | /*
107 | Single Beer View
108 | */
109 |
110 | .single-beer
111 | max-width 500px
112 | margin 0 auto
113 | background white
114 | padding 2rem
115 | .label
116 | width 100%
117 | img
118 | max-width 100%
119 |
120 | h3
121 | font-size 2rem
122 |
123 | .glass
124 | img
125 | float left
126 | width 200px
127 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var markdown = require('gulp-markdown');
3 | var zip = require('gulp-zip');
4 |
5 | var fs = require('fs');
6 | var notesTop = fs.readFileSync('notes/top.html');
7 | var notesBottom = fs.readFileSync('notes/bottom.html');
8 | var insert = require('gulp-insert');
9 |
10 | gulp.task('notes',function() {
11 | return gulp.src('notes/**/*.md')
12 | .pipe(markdown({
13 | gfm: true
14 | }))
15 | .pipe(insert.prepend(notesTop))
16 | .pipe(insert.append(notesBottom))
17 | .pipe(gulp.dest('notes-export'))
18 | .pipe(zip('gulp-notes.zip'))
19 | .pipe(gulp.dest('.'));
20 | });
21 |
22 | // gulp.task('exercises', function() {
23 | // gulp.src('./CODE/*')
24 | // .pipe(zip('gulp-exercises.zip'))
25 | // .pipe(gulp.dest('.'))
26 | // });
27 |
28 | gulp.task('default',['notes']);
29 |
--------------------------------------------------------------------------------
/notes/01 - Initial React Setup.md:
--------------------------------------------------------------------------------
1 | ## Env Setup
2 |
3 | In order to write React code, we need to have a little tooling in place.
4 |
5 | React is written in ES6 modules. Rather than use a bunch of Script tags and dump them into our code, we use ES6 modules to import and export our components. Much more on this as we go along.
6 |
7 | In order to work with modules, we need a few things:
8 |
9 | 1. **npm** — Used for installing our dependencies - like React!
10 | 2. **node.js** — the tooling we use runs on Node.js
11 | 3. **Webpack** — one of the most popular bundlers for JavaScript modules. We will be using Webpack behind the scenes with a command line tool called `create-react-app`.
12 | 4. **Terminal** — the bundling of our code happens in the Terminal so you'll need either the OSX terminal app or the Windows PowerShell installed
13 |
14 | So, let's head on over to our `beer-me` directory. The `package.json` file already includes the dependencies that we need, so we just need to run `npm install` to fetch and install them.
15 |
16 | Our webpack process is setup for using `index.js` as our entry point. Open it up and type:
17 |
18 | ```js
19 | alert('it works!');
20 | ```
21 |
22 | ## Running our App
23 |
24 | To start the app, simply run `npm start`. Webpack (via `create-react-app`) will build the site and open it up in your browser. Since all we have written so far is just a alert, you should see it when the browser opens.
25 |
26 | 
27 |
28 | ## Hello World in React
29 |
30 | Let's get a paragraph tag displayed on the page.
31 |
32 | First we need to import React and something called react-dom which will allow us to display HTML.
33 |
34 | Since both `react` and `react-dom` are both npm packages we we already installed, we can simply require the entire React lib, and then use curly brackets to import just the one `render` method from `react-dom`:
35 |
36 | ```js
37 | import React from 'react';
38 | import { render } from 'react-dom';
39 | ```
40 |
41 | Next, we need to render out something. React needs an entry point where it can "mount" itself. If you look at `index.html` you'll see that we have an empty div with the id of `root`.
42 |
43 | ```
44 | render(
Hello World
, document.querySelector('#root'));
45 | ```
46 |
47 | Let's break this down:
48 |
49 | 1. `render()` is the method we just imported
50 | 2.
Hello World
is the element we wish to render — this is really something called JSX. More on this in a bit! It's important to note thay we don't have to put it in quotes.
51 | 3. document.querySelector('#root') finds the entry point in the DOM
52 |
53 | Save and refresh your page, you should see the very basic of getting up and running with React. If you open your dev tools now you should see the React tab which you can now inspect:
54 |
55 | 
56 |
57 | ## CSS
58 |
59 | We have two options for using CSS with a React app.
60 |
61 | 1. You can load in a `style.css` file all on your own into your index.html and not have React or Webpack play any part of it.
62 | 2. You can let Webpack handle importing the CSS for you. The advantages of this is that you _could_ write CSS the directly relates to each component and only have it imported as you need it.
63 |
64 | This is a very divisive topic in the web development community so I'll let you make your own decisions here. For us, I'm going to import all our CSS at the top level app.
65 |
66 | In your `index.js`, place this just above your render:
67 |
68 | ```js
69 | /* Import CSS */
70 | import css from './style.css';
71 | ```
72 |
73 | I'm using plain cSS here - but you could use Sass, Less, Post CSS or regular CSS if you'd like.
74 |
75 | However, since this all happens inside the WebPack file with something called _loaders_, we would need to _eject_ from `create-react-app`. More on this later.
76 |
--------------------------------------------------------------------------------
/notes/02- Our First React Component.md:
--------------------------------------------------------------------------------
1 | ## Our First React Component
2 |
3 | Now - we can't just be writing all of our HTML inside of the render method — we need to break things up into their own components. Let's write our first React component.
4 |
5 | In React, everything is a component. You can think of our application as many parts that we piece together to make an application as a whole.
6 |
7 | Let's look at the front page of our app. What are the components we are going to make?
8 |
9 | 
10 |
11 | Let's start with a `Header.js` component. In react, since components are sort of like classes that can be reused over and over, it's a standard practice to name your files and components with a capital.
12 |
13 | So in a folder named `components`, create `components/Header.js`.
14 |
15 | Now there are a few things we need to create a component:
16 |
17 | 1. React itself
18 |
19 | ```js
20 | import React from 'react';
21 | ```
22 |
23 | Notice that even though we have imported React into `index.js`, we also import it in here. Modules are not global and you _must_ re-import the react library into every point that you need it.
24 |
25 | Then we create our component and store it in a variable. We extend `React.Component` to create the react class here.
26 |
27 | ## Quick Aside
28 |
29 | We are extending React.Component here with ES6 classes. Since ES6 classes currently fall short of a few much needed properties, we will be using some features that are "soon-to-come" to JavaScript to fill in those holes.
30 |
31 | The major shortcoming of ES6 classes and React is that the keyword `this` is not bound to the component instance. To get around this, we will be using [class properties](https://babeljs.io/docs/plugins/transform-class-properties/) which are not yet in the language but will be compiled down for us. I'll be sure to touch on this as we learn, but this is a heads up that you may see some JS syntax that you have never encountered before.
32 |
33 | ## Back to it
34 |
35 | Every React component will have multiple methods that live inside it, but the one method that we absolutely need is the `render()` method. This is a pre-defined method that React looks for when it displays the content on our page.
36 |
37 | Let's type this one together to get the hang of the syntax and answer the following questions along the way:
38 |
39 | 1. Why do we use `const`?
40 | 2. What is this `render() {}` business? should it not be `render: function() {}`?
41 | 3. Why is there (parens) around everything?
42 |
43 | ```js
44 | class Header extends React.Component {
45 | render() {
46 | return (
47 |
Beer Me!
48 | )
49 | }
50 | };
51 | ```
52 |
53 | Finally, since we will need to import this component into other components, we need to **export it** with `export default Header`.
54 |
55 | Our final code looks like this:
56 |
57 | ```js
58 | import React from 'react';
59 |
60 | class Header extends React.Component {
61 | render() {
62 | return (
63 |
Beer Me!
64 | )
65 | }
66 | };
67 |
68 | export default Header;
69 |
70 | ```
71 |
72 |
73 | ### Displaying ``
74 |
75 | We have now made a Header component. Swap out the `
Hello World
` with our new Header component in `index.js`. We can use the "tag" `` to reference our component:
76 |
77 | ```js
78 | render(, document.querySelector('#root'));
79 | ```
80 |
81 | But you'll see that we get this error:
82 |
83 | 
84 |
85 | That is because we haven't yet **imported** our component into index.js!
86 |
87 | ```js
88 | import Header from './components/Header';
89 | ```
90 |
91 | Since the component is not part of our `node_modules` directory, we do a relative reference with `./`. Refresh your page and you should now see the title:
92 |
93 |
94 | 
95 |
--------------------------------------------------------------------------------
/notes/03 - Say Hello to JSX.md:
--------------------------------------------------------------------------------
1 | ## Say Hello to JSX
2 |
3 | JSX is the templating language we write in order to create HTML or DOM fragments. It's not technically JavaScript, but Babel and Webpack are able to convert it from the nice syntax that we know and understand into the JavaScript equivalent of `React.createElelement`.
4 |
5 | Writing JSX is very similar to JavaScript except for a few items.
6 |
7 | ### Self Closing Elements
8 |
9 | Elements **must** close. This isn't an issue for most tags:
10 |
11 | ```html
12 |
Beer Me!
13 | ```
14 |
15 | But self-closing elements like `input` and `img` need to have the closing `/` in them:
16 |
17 | ```html
18 |
19 |
20 |
21 |
22 |
23 | ```
24 |
25 | ### Must return only one element
26 |
27 | React expects you to only return one element. You can have as many child elements as you wish, but only one top level element.
28 |
29 | So, if I needed to return the following HTML. You'll get an error saying `Adjacent JSX elements must be wrapped in an enclosing tag (8:6)`.
30 |
31 | ```html
32 |
Beer Name
33 |
Beer Description goes here
34 | ```
35 |
36 | An easy fix is to wrap it in a div:
37 |
38 | ```html
39 |
40 |
Beer Name
41 |
Beer Description goes here
42 |
43 | ```
44 |
45 | One major downside to this is that it interferes with CSS where the parent-child relationship is important. Extra divs can really goof up your Flexbox or CSS Grid code.
46 |
47 | As of React 16.2, we are now able to use something called Fragments which won't render out a containing div:
48 |
49 | ```js
50 | <>
51 |
Beer Name
52 |
Beer Description goes here
53 | >
54 | ```
55 |
56 | ### Comments
57 |
58 | Comments are a little weird in JSX, since we are _technically_ writing JavaScript, we need to use JavaScript comments, but inside JSX curly braces.
59 |
60 | So a comment in JSX looks like
61 |
62 | ```html
63 |
64 | {/* Comment here */ }
65 |
Beer Name
66 |
Beer Description goes here
67 |
68 | ```
69 |
70 | Another gotcha is that you may not have a comment at the top level, for the same reason we can only return one element:
71 |
72 | ```html
73 | {/* Comment cannot go here */ }
74 |
75 |
Beer Name
76 |
Beer Description goes here
77 |
78 | ```
79 |
80 | ### className instead of class
81 |
82 | Because `class` is a reserved word in JavaScript, you must use className instead.
83 |
84 | ```html
85 |
Hello
86 | ```
87 |
88 | is now
89 |
90 | ```html
91 |
Hello
92 | ```
93 |
94 | If you use Emmet, your expansions will be automatically updated for you.
95 |
--------------------------------------------------------------------------------
/notes/04 - Our Second React Component .md:
--------------------------------------------------------------------------------
1 | ## Our Second React Component
2 |
3 | Before we can display that component, we need a wrapper component that will hold all everything.
4 |
5 | At the end of the day, our JSX is going to look similar to this:
6 |
7 | ```html
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ```
20 |
21 | Create a component called `Main.js` in your components directory.
22 |
23 | In that file, we will need to do the same three things we do in every component:
24 |
25 | 1. Import React
26 | 2. Create the component with `React.createClass()`
27 | 3. Export your Component
28 |
29 | ```
30 | import React from 'react';
31 | import Header from './Header';
32 |
33 | class Main extends React.Component {
34 | render() {
35 | return (
36 |
37 |
38 |
39 | )
40 | }
41 | };
42 |
43 | export default Main;
44 | ```
45 |
46 | Now in `index.js` import Main: `import Main from './components/Main';` and switch out `` with ``.
47 |
48 | See where we are going with this? We indirectly display the `` because it's part of ``. Take a look at your React dev tools now.
49 | N
50 | 
51 |
--------------------------------------------------------------------------------
/notes/05 - Passing Props.md:
--------------------------------------------------------------------------------
1 | ## Passing Props
2 |
3 | In React, there are two main ways in which you can provide data to a component — **state** and **props**. We're going to learn about props and later on, state.
4 |
5 | Our Header.js file is hard coded with "Beer Me!" — what if we had that data at the Main component and want to pass it down to Header?
6 |
7 | **To pass down data to a child component, we use props.**
8 |
9 | Props look just like attributes. Let's change our `` to take a prop called `siteName`.
10 |
11 | To pass the prop, simply pass it like an attribute:
12 |
13 | ```html
14 |
15 | ```
16 |
17 | You can pass any type down - strings, numbers, methods. Anything other than a string will need curly brackets:
18 |
19 | Pass down a property:
20 |
21 | ```html
22 |
23 | ```
24 |
25 | Use a function:
26 |
27 | ```html
28 |
29 | ```
30 |
31 | Pass a function
32 |
33 | ```html
34 |
35 | ```
36 |
37 | We will go more in depth on this soon!
38 |
39 | ### Using Props
40 |
41 | Once props are passed _to_ a component, we can use them access them inside that component. If you inspect your `` component with react dev tools you should now see that the there is an object inside the component called `props`:
42 |
43 | 
44 |
45 | We can access the props inside our render component by using `this.props.siteName`.
46 |
47 | ```html
48 |
55 | ```
56 |
57 | A few things to note:
58 |
59 | 1. Notice how we use `{curly braces}` to access variables inside our JSX?
60 | 2. React automatically binds all methods inside the component to the Component itself. What does that mean? In any method (render for this case) `this` refers to ``.
61 |
62 |
63 | ## PropTypes
64 |
65 | When using components, you don't always know which props you should be using. If a co-worker created the component, how do you know which props it requires?
66 |
67 | PropTypes will check that the necessary data, and the type of data being passed to the component is done correctly.
68 |
69 | So, anytime you use props, your component must have propTypes.
70 |
71 | Let's take `` in `Header.js` for example. What props do we use there?
72 |
73 | ```
74 | class Header extends React.Component {
75 | render() {
76 | return (
77 |
{this.props.siteName}
78 | );
79 | }
80 | };
81 | ```
82 |
83 | `siteName`!
84 |
85 | And what type is it? A String!
86 |
87 | First we need to import `PropTypes` from the `prop-types` package.
88 |
89 | ```
90 | import PropTypes from 'prop-types'
91 | // you can also import just the items you need:
92 | import { string, number } from 'prop-types';
93 | // you may also see an older style React.PropTypes. This is deprecated
94 | import { PropTypes } from 'react';
95 | ```
96 |
97 |
98 | So, we can add propTypes to our component:
99 |
100 | ```
101 | class Header extends React.Component {
102 |
103 | static propTypes = {
104 | siteName: PropTypes.string.isRequired
105 | }
106 |
107 | render() {
108 | return (
109 |
{this.props.siteName}
110 | );
111 | }
112 | };
113 | ```
114 |
115 | A few things:
116 | 1. We use `static propType =` to set the property on the component.
117 | 2. there is no `,` after the object or method. This is part of ES6 Classes and differs from an object where you must put a `,`
118 |
119 | This will now error in your console if you:
120 |
121 | 1) Forget to pass something
122 | 2) Pass a number, boolean, function or object.
123 |
124 | More info on all the available propTypes here:
125 |
126 |
127 |
--------------------------------------------------------------------------------
/notes/06 - Exercise.md:
--------------------------------------------------------------------------------
1 | ## Exercise: A few More Components
2 |
3 | We need to create a few more components and scaffold out our application.
4 |
5 | 1. A **Loader.js** component which will take a prop of "message" and display it. Your HTML should look like this:
6 |
7 | ```html
8 |
9 |
10 |
Your Message from props goes here
11 |
12 | ```
13 |
14 | 2. A **Beer.js** component, for now just render out `
Beer will go here
`
15 | 3. A **Results.js** component, for how just render out `
Results go here
`
16 | 4. A **Search.js** component, for now just render out `
Search goes here
`
17 | 4. A **Single.js** component, for now just render out `
A single beer goes here
`
18 |
--------------------------------------------------------------------------------
/notes/07 - React Router.md:
--------------------------------------------------------------------------------
1 |
2 | ## Routing with React Router
3 |
4 | Our application is going to have three "routes", or urls that a user can hit.
5 |
6 | 1. `localhost:3000/` for the home page and default beers
7 | 1. `localhost:3000/search/angry` the search results for a specific keyword. In this case `angry`
8 | 1. `http://localhost:3000/beer/OUqh1N/Texas-Craft-Brewers-Guild` the single view page for a specific beer - we pass along the beer ID and the beer name in the url.
9 |
10 | React is just a view library so it doesn't have routing built in like other frameworks might have. That said, the community has primarily standardized on using [React Router](https://github.com/ReactTraining/react-router)
11 |
12 | React router is just a component in itself - it watches for history changes and will render out the correct component for you.
13 |
14 | On every page we want to render either the `` component on the Home page and search page or the `` on the single beer page.
15 |
16 |
17 | ### Step 1: Import react-router
18 |
19 | There are a number of things we need from the `react-router-dom` package. In `index.js`, import the following:
20 |
21 | ```js
22 | import { BrowserRouter, Route } from 'react-router-dom';
23 | ```
24 |
25 | ### Step 2: Update our index.js Render
26 |
27 | In `index.js` we are going to change our out render function to take the router instead of the `` component.
28 |
29 | Currently it looks like this:
30 |
31 | ```html
32 | render(, document.querySelector('#root'));
33 | ```
34 |
35 | And we will update it to look like this. Let's code it together and talk though each of the parts.
36 |
37 | ```
38 | const Root = function() {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | );
48 | };
49 |
50 | render(, document.querySelector('#root'));
51 | ```
52 |
53 | **Finally,** you'll notice we are referencing the `Results` and `Single` components here. Make sure you import them at the top of your document alongside the already imported `Main`:
54 |
55 | Our entire `index.js` now looks like:
56 |
57 | ```html
58 | import React from 'react';
59 | import { render } from 'react-dom';
60 | import Main from './components/Main';
61 | import Single from './components/Single';
62 |
63 | import { BrowserRouter, Route } from 'react-router-dom';
64 |
65 | /* Import CSS */
66 | import './style.css';
67 |
68 | const Root = function() {
69 | return (
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | );
78 | };
79 |
80 | render(, document.querySelector('#root'));
81 | ```
82 |
83 | ### Step 3: Try it out
84 |
85 | You should be able to go to any of the matching routes:
86 |
87 | *
88 | *
89 | *
90 |
--------------------------------------------------------------------------------
/notes/08 - Say Hello To State.md:
--------------------------------------------------------------------------------
1 | ## Say Hello To State
2 |
3 | Up until this point, React may just seem like a glorified templating engine. This is the section where you may have an "Ah-Ha!" moment and understand why React shines.
4 |
5 | React components have a concept called **state** where we hold data related to our application. State is the the _source of truth_ for our components and whenever _anything_ in state changes, the application's DOM will update itself.
6 |
7 | If you are coming from a jQuery background, this is the real power of React. Rather than manually update the DOM ourselves when something happens, we simply just update this big object called **state** and react will figure out which parts of the DOM need to be updated!
8 |
9 | We will use state to hold a list of beers, but before we even do that, let's do a simple example together.
10 |
11 | We're going to make a simple beer counter - a button, that when clicked, will increment the number of beers you need to buy for a party. Essentially just a button that will increment every time you click it. Very simple, but will illustrate a few core concepts that we will use for this application.
12 |
13 | ### Step 1: Set Initial State
14 |
15 | When a component mounts, we need what is called the initial state — what the data will start with. We can update this state or load in external data in just a second, but for now we need to declare the very first state.
16 |
17 | This is done by setting state in the classes constructor. The class constructor is a method that will run as the class is created. We can set our state in here.
18 |
19 | Let's start with 10 beer:
20 |
21 | ```js
22 | constructor() {
23 | super();
24 | this.state = {
25 | numBeers : 10
26 | }
27 | }
28 | ```
29 |
30 | ### Step 2: Display The State in JSX
31 |
32 | Right below the `{this.state.numBeers} 🍺
36 | ```
37 |
38 | See how we used `{this.state.numBeers}`? Refresh your page and you should see
39 |
40 | 
41 |
42 | ### Step 3: Create a method to update that number
43 |
44 | Along with the built in methods like `render`, we can create our own methods that will handle the updating of state. We will create a method on Main called `incrementBeers`. Note how we set the instance property with the new syntax of `incrementBeers = () => {`. This allows us to reference `this` inside the method and have `this` be equal to that instance of the react component. A regular method will not be bound to the React component unless you explicitly bind it in the constructor.
45 |
46 | ```js
47 | incrementBeers = () => {
48 | // create a new upated state variable
49 | const beerAmount = this.state.numBeers + 1;
50 | // set state to that amount
51 | this.setState({ numBeers: beerAmount });
52 | };
53 | ```
54 |
55 | Now whenever that method is run, state will be incremented by one. Notice how we are explicitly setting state here and not just `this.state.numBeers++`.
56 |
57 | ### Step 4: Hook up that method to the button
58 |
59 | Finally, the way we handle clicks and other events in React is through the `onClick` event handler. React has re-implemented all of JavaScripts inline events so they may seem familiar.
60 |
61 | ```html
62 |
63 | ```
64 |
65 | ### Step 5: Click away
66 |
67 | Go ahead and click that button a few times. When you click it, it triggers `incrementBeers`, which will update state. When state is updated, React knows we are referencing `{this.state.numBeers}` and will update our button for us.
68 |
69 | That there is the key to understanding how react works. You update your data, not your DOM/HTML.
70 |
71 |
72 |
73 | ## Updating State with Ajax
74 |
75 | Now it's time to pull in a list of beers from our Beer API.
76 |
77 | ### Step 1: Initial State
78 |
79 | We start by applying an empty array of beers to the initial state. Our `constructor` should now look like this:
80 |
81 | ```js
82 | constructor() {
83 | super();
84 | this.state = {
85 | numBeers : 10,
86 | beers: []
87 | }
88 | }
89 | ```
90 |
91 | Note: We can also specify state directly on our component now. This is exactly the same as the above with a bit of a cleaner syntax:
92 |
93 | ```js
94 | class Wes extends React.Components {
95 | state = {
96 | name: "Wes"
97 | };
98 |
99 | render() {
100 | return
Hello my name is {this.state.name}
;
101 | }
102 | }
103 | ```
104 |
105 | ### Step 2: Fetch JSON
106 |
107 | The endpoint we are going to hit is
108 |
109 | Where the `q` param will either be `hops` by default, or something inputted by the user. Since the listing of beers will be attached to the Main component, we can create a new method called `loadBeers()` on that component.
110 |
111 | We will be using the new browser api `fetch()` to go and grab this data. It works very similar to jquery's `$.getJSON()`.
112 |
113 | Again, try not to copy/paste but rather let's work through the following code ourselves:
114 |
115 | ```js
116 | loadBeers = (searchTerm = "hops") => {
117 | fetch(`http://api.react.beer/v2/search?q=${searchTerm}&type=beer`)
118 | .then(data => data.json())
119 | .then(beers => {
120 | console.log(beers);
121 | // filter for beers with images
122 | const filteredBeers = beers.data.filter(beer => !!beer.labels);
123 | this.setState({ beers: filteredBeers });
124 | })
125 | .catch(err => console.error(err));
126 | };
127 | ```
128 |
129 | ### Step 3: Trigger on load
130 |
131 | We need to trigger this `fetchBeers()` method on load. For this, we will use another one of React's lifecycle methods called `componentDidMount()` :
132 |
133 | ```js
134 | componentDidMount() {
135 | this.loadBeers();
136 | }
137 | ```
138 |
139 | Refresh the page and the Ajax request should trigger. If all went well you should see a list of beers in your component's state:
140 |
141 | 
142 |
143 | ## Displaying State with JSX
144 |
145 | Now we have data in our state how do we display it?
146 |
147 | We would like to display the results in `Results.js`, but we fetched the beer in `Main.js`.
148 |
149 | **How do we get data from one component to another?**
150 |
151 | We pass it via props.
152 |
153 | First, we need to display the `` component. We don't always want to show the results component, just on the home page and the search page.
154 |
155 | Now, normally we would just render the component like this:
156 |
157 | ```html
158 |
159 | ```
160 |
161 | **A few things to note here: **
162 |
163 | 1. `` is our results component that we make
164 | 2. `{...this.state}` take our entire state from `` and passes it down to ``. This is called an Object Spread and is used quite a bit in React.
165 | 3. It's important to note that it's not ideal to pass everything down to children _unless you need it_. If you would like to cherry pick pieces of state, we could pass them down like so:
166 |
167 | ```html
168 |
169 | ```
170 |
171 | If you look at the Results component in React dev tools, you'll see that the beers are available under props. Search for a `` component in dev tools and you'll see
172 |
173 | 
174 |
175 | **Why props?** When something is passed down from a parent (``) to a Child (``), it is passed down via props. So we surface it via `this.props.beers` inside the component that receives it.
176 |
177 | In `Results.js`, let's see what we are dealing with. Dump your beers with this:
178 |
179 | ```html
180 |
{JSON.stringify(this.props.beers,null,' ')}
181 | ```
182 |
183 | We can see the data, but we need to loop over all the results and then display them. Remember this image?
184 |
185 | 
186 |
187 | JSX does not have any logic to handle the looping over the beers - we need to use a JavaScript map to loop over each one and return yet another component — `` for each.
188 |
189 | First import the Beer component that will be used to display each of the listed beers:
190 |
191 | ```js
192 | import Beer from "./Beer";
193 | ```
194 |
195 | Then we loop over each beer and return a beer component.
196 |
197 | ```html
198 |
199 | {this.props.beers.map(details => )}
200 |
201 | ```
202 |
203 | A few things to note here:
204 |
205 | 1. `this.props.beers` is an array so we can use map with an ES6 arrow function
206 | 2. We pass the each beer result to the `` component via a details prop
207 | 3. Each time you have multiple of the same component you need to supply a `key` prop to help React differentiate them in the DOM
208 |
--------------------------------------------------------------------------------
/notes/09 - Caching Beers in LocalStorage.md:
--------------------------------------------------------------------------------
1 | ## Caching Beers in LocalStorage
2 |
3 | You may notice that everytime we go back to the main page we need to re-load all the beers with our Ajax endpoint - and this is pretty slow.
4 |
5 | We can store the beer data in localStorage and when someone searches for "hops" or "ale", we will first check to see if we have the results in localStorage.
6 |
7 | In `Main.js`, above let's update our `loadBeers()` method
8 |
9 | ```diff
10 | loadBeers = (searchTerm = 'hops') => {
11 | + // Check for beers in local storage
12 | + const localStorageBeers = localStorage.getItem(`search-${searchTerm}`);
13 | + if (localStorageBeers) {
14 | + const localBeers = JSON.parse(localStorageBeers);
15 | + this.setState({ beers: localBeers, loading: false });
16 | + return; // stop before fetch happens!
17 | + }
18 |
19 | fetch(`http://api.react.beer/v2/search?q=${searchTerm}&type=beer`).then(data => data.json())
20 | .then((beers) => {
21 | console.log(beers);
22 | // filter for beers with images
23 | this.state.beers = beers.data.filter(beer => !!beer.labels);
24 | this.setState({beers : this.state.beers, loading:false });
25 | + // save to local storage in case we search for this again
26 | + localStorage.setItem(`search-${searchTerm}`, JSON.stringify(this.state.beers));
27 | })
28 | .catch(err => console.log(err))
29 | ```
30 |
--------------------------------------------------------------------------------
/notes/10 - Working in Beer-js.md:
--------------------------------------------------------------------------------
1 | ## Working in Beer.js
2 |
3 | So far we have passed all beers from `Main.js` → `Results.js` and passed each individual beer from `Results.js` → `Beer.js`.
4 |
5 | Working in `Beer.js`, how do we access the info about that beer? How did we pass it? **Props!** What was it called? **details**!
6 |
7 | So how do we access it inside `Beer.js`? `this.props.details`!
8 |
9 | First, let's import a few dependencies:
10 |
11 | ```js
12 | import slug from 'slugify';
13 | import { Link } from 'react-router-dom';
14 | ```
15 |
16 | Then work through the render function together:
17 |
18 | ```html
19 | render() {
20 | const { name, labels, id } = this.props.details;
21 | const image = labels ? labels.medium : 'notfound.jpg';
22 |
23 | return (
24 |
25 |
26 |
{name}
27 |
28 |
29 |
30 | );
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/notes/11 - Adding a Loading State.md:
--------------------------------------------------------------------------------
1 | ## Introducing a loading State
2 |
3 | A common task in applications is to show a loading indicator. We can use use a boolean stored on the state of ``.
4 |
5 | ```diff
6 | constructor() {
7 | this.state = {
8 | numBeers : 10,
9 | beers: [],
10 | + loading: true
11 | }
12 | }
13 | ```
14 |
15 | Then update our state inside the `loadBeers` method:
16 |
17 | ```diff
18 | loadBeers = (searchTerm = 'hops') => {
19 | + this.setState({ loading: true });
20 |
21 | // Check for beers in local storage
22 | const localStorageBeers = localStorage.getItem(`search-${searchTerm}`);
23 | if (localStorageBeers) {
24 | const localBeers = JSON.parse(localStorageBeers);
25 | this.setState({ beers: localBeers, loading: false });
26 | return; // stop before fetch happens!
27 | }
28 |
29 | fetch(`http://api.react.beer/v2/search?q=${searchTerm}&type=beer`).then(data => data.json())
30 | .then((beers) => {
31 | console.log(beers);
32 | // filter for beers with images
33 | const filteredBeers = beers.data.filter(beer => !!beer.labels);
34 | + this.setState({ beers: filteredBeers, loading: false });
35 | // save to local storage in case we search for this again
36 | localStorage.setItem(`search-${searchTerm}`, JSON.stringify(this.state.beers));
37 | })
38 | .catch(err => console.error(err));
39 | }
40 |
41 | ```
42 |
43 | Now in `Results.js` we can first check if the results are loading and display the `` accordingly:
44 |
45 | Require it:
46 |
47 | ```js
48 | import Loader from './Loader';
49 | ```
50 |
51 | and amend your render:
52 |
53 | ```diff
54 | render() {
55 | + if(this.props.loading) {
56 | + return
57 | + }
58 |
59 | return (
60 |
65 | )
66 | }
67 | ```
68 |
69 | Finally, you can update your Loader.js to take the message prop:
70 |
71 | ```diff
72 | -
Your Message from props goes here
73 | +
{this.props.message}
74 | ```
75 |
--------------------------------------------------------------------------------
/notes/12 - Exercise- Working with a Single-js.md:
--------------------------------------------------------------------------------
1 | ## Exercise: Working with a Single.js
2 |
3 | When you click on a beer you should see this:
4 |
5 | 
6 |
7 | We need to transform Single.js to display all the info about that beer. We are going to build Single.js as an exercise where you work with each other. Here are some notes:
8 |
9 | 1. Your Single.js will have 2 items in state: `beer` and `loading`
10 | 2. You will fetch the beer's info in a `loadBeer` method that you create for `Single.js` from `http://api.react.beer/v2/beer/${beerId}`.For example
11 | 3. You can access the URL variables (Like `beerId`) in `this.props.match.params`
12 | 4. Display the Loader with the words "Pouring a cold one!" while the Ajax request is working
13 | 5. Display at least the Beer name and the Beer description. We will work through the other bits together.
14 |
15 | The final output will look like this:
16 |
17 | 
18 |
--------------------------------------------------------------------------------
/notes/13 - Working with Forms + Search.md:
--------------------------------------------------------------------------------
1 | ## Working with Forms + Search
2 |
3 | The final piece of the puzzle is the search form. Let's start off by creating the markup for it in `Search.js`
4 |
5 | ```html
6 |
7 |
11 |
12 | ```
13 |
14 | The only thing that may seem out of place here so far is the `ref={(q) => this.q = q}` — we will reference this in just a second.
15 |
16 | When someone submits that form, we need to:
17 |
18 | 1. Stop the form from submitting
19 | 2. Get the value of the input
20 | 3. redirect them to `/search/whatever-they-searched-for`
21 |
22 | Let's take these three things and tackle them step by step:
23 |
24 | ### Stop the form from submitting
25 |
26 | How would you normally stop a form from submitting?
27 |
28 | You listen to for the `submit` event and call `preventDefault` on the event!
29 |
30 | So, we will modify our form tag: `