wrapping up lesson 4

This commit is contained in:
Micah Godbolt
2019-03-03 20:24:20 -08:00
parent adf8f76c5e
commit b9089ab8b6
11 changed files with 308 additions and 289 deletions

View File

@@ -1,220 +0,0 @@
# Step 4 - Introduction To React Demo
In this demo we'll be creating a simple counter that will display a count and increment on click.
## React Hello World
```js
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<p>Hello World</p>, document.getElementById('app'));
```
- `import React from 'react';` - This is how we [import modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) in JavaScript. This line creates a variable in this file called `React` that is equal to the default `export` of the `react` npm module.
- `import ReactDOM from "react-dom";` - We've seen React imported before, but now we're also grabbing `ReactDOM` from a package called `react-dom`.
- `ReactDOM.render()` - This function is how our code gets on the page. The function takes two parameters, the content to place on the page, and the location that you want it placed.
## Writing a React Component
A React component is a piece of code that returns a portion of your application. This can include HTML markup, CSS styles as well as JavaScript driven functionality.
Components can be created in two ways. The first is method is to use a [JavaScript class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes), which extends (inherits from) the `React.Component` class.
Classes in JavaScript provide a way to collect methods(functions) and properties(values) in an extendable container. We extend `React.Component` because it provides us with several built-in methods, including `render`.
```jsx
export class App extends React.Component {
render() {
return <p>Hello World</p>;
}
}
```
Moving our "Hello World" markup into our App's `render` function, we can now update the `ReactDOM.render()` function to look like this:
```jsx
ReactDOM.render(<App />, document.getElementById('app'));
```
```jsx
export class App extends React.Component {
render() {
const text = 'My App';
return (
<div className="App">
<h2>{text != '' ? text : 'Default App Name'}</h2>
</div>
);
}
}
```
- `import React from 'react';` - Each file needs to import React, but only one copy of the code is included in your application.
- `export class App` - Just like React exports code, our App component exports a class called `App`. This allows us to import the class into other files.
- `extends React.Component` - A JavaScript class is similar to a class in other programming languages (it's a collection of methods and properties). Classes can also be extended, so when we create a React component class, we always extend the base `React.Component` class. (Note that this `Component` class is coming from the `React` variable imported up top.)
- `render()` - One of the methods defined by `React.Component` is the `render()` method, which defines the HTML the component is going to render.
- `return` - Remember that functions can return values in addition to having side effects, and this component is no different.
**Inside of the return?** It's HTML! Actually, it's [JSX](https://reactjs.org/docs/introducing-jsx.html), but with very few exceptions you can treat it like HTML. A few key differences:
- Since `class` is a [reserved word](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords) in JavaScript, you will need to use `className` on your HTML tags: `<div className="foo">`
- We can use custom HTML tags corresponding to the React components we create: `<div><MyControl>hi</MyControl></div>`
- Controls can be self-closing: `<div><MyControl text='hi' /></div>`
- You can use JavaScript inside of JSX!
## Counter Component
In this example we'll start with an already scaffolded-out control. The goal of our counter is to track how many times the counter button is clicked.
React allows each control to specify its own data store, called **state**. We can reference values in state when we render our UI, and we can also update state over the lifetime of our application.
### Adding State
JavaScript uses a `constructor` method to instantiate each copy of a class. So for class-based controls, this is where we define an initial value for `state`.
```js
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
```
- The constructor takes in the component's `props` (values passed into the control).
- The [`super()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super) function calls the constructor of the parent class (in this case `React.Component`) to do any shared setup.
- Now we can define any state variables we want to use in the control and give them a default value. Our counter value can now be accessed via `this.state.counter`. Later, we can update state by calling `this.setState({ counter: 1 })`.
#### Using [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) for props and state
Both `props` and `state` are JavaScript objects. They have a bunch of key/value pairs in them which you can access via `this.props.foo` or `this.state.bar`. Sometimes they have MANY values inside of them which you need access to. You could do this:
```js
let cat = this.props.cat;
let dog = this.props.dog;
let bird = this.props.bird;
let pig = this.props.pig;
let cow = this.props.cow;
```
> Note that we access `props` and `state` on `this`, which is how you reference all class properties and methods.
But this is verbose and repetitive. Instead you can use destructuring to turn this into a one-liner.
```js
let { cat, dog, bird, pig, cow } = this.props;
```
Even though this isn't 100% necessary today, it does future-proof our code if we add more values to `props` or `state` later. So let's add this inside of the `render` method, above the `return`:
```js
const { counter } = this.state;
const { text } = this.props;
```
### Adding JSX
```jsx
return (
<div>
{text}: {counter}
<button>Click</button>
</div>
);
```
Each JSX return value needs to be a single element, so start with a wrapping `<div>`. Inside of that we can add the `text` we get from `this.props`, then after a colon, the `counter` we pulled in from `this.state`. This will render as the string `My Text Prop: 0`. After that let's add a button we'll use later.
Now let's see how we can use this component in our app.
### Updating the App to Use Counters
Before we can use our `Counter`, we need to import it into the App file.
```js
import { Counter } from './components/Counter';
```
Now that we have access to `Counter`, we can use it in the App just as if it were an HTML element.
```jsx
return (
<div>
<h2>My App</h2>
<Counter text="Chickens" />
<Counter text="Ducks" />
</div>
);
```
> Note the capitalization of `Counter`. HTML might not be case-sensitive, but JSX is! A common practice is to use the capitalized names of HTML elements to name corresponding React components: Button, Select, Label, Form, etc.
## Exploring Component Props
Now that we've got two counters on our page, we can see that the string passed into the `text` attribute got passed into our counter and rendered on the page. Being able to pass values (props) into controls makes them more flexible and reusable. Props can be strings, numbers, booleans, and even arrays and objects.
```jsx
<MyComponent
open={false}
count={5}
text="Hi there"
items={['cat', 'dog', 'bird']}
config={{
start: 1,
end: 10,
autoStart: true
}}
/>
```
> Note that all non-string values are passed through as JavaScript by wrapping them in `{}`.
### Writing our Button Click Handler
Our next step is to wire up the button to increment the `counter` in our component state. This will very similar to what we did in step 3, but instead of placing the function in a script tag, we can create it as a class method, and keep it out of the global scope.
> By convention we place other methods below `render()`, and private methods (those for internal use only) are prefixed with an underscore.
This function will update our component's state, incrementing the counter value by 1. (Note that `setState` only modifies the values of keys listed in the object passed as its parameter.)
```jsx
_onButtonClick = () => {
this.setState({
counter: this.state.counter + 1
});
};
```
> Note that this could also be written as `this.setState(prevState => ({ counter: prevState.counter + 1 }));` to ensure that state is not updated until the previous state has been determined
Now that we have a function to increment our count, all that's left is to connect it to our button.
```jsx
<button onClick={this._onButtonClick}>Click</button>
```
> Also note that each `Counter` maintains its own state! You can modify the state inside of one counter without affecting the others.
## Using a Button component
Buttons are among the most commonly written components. Custom buttons help abstract common styling, add icons or other decorations, and increase functionality (menu buttons etc). Let's take a quick look at a custom button component to see how it comes together.
```jsx
import React from 'react';
import './Button.css';
export const Button = props => {
return (
<button className="Button" onClick={props.onClick}>
{props.children}
</button>
);
};
```
- All components need to import React (don't worry, only one copy ever gets into your app)
- CSS files imported into the component are only loaded if the component is used
- React components can be created as a class **or** as a function. In this function component, props are passed in as a function parameter.
> Until recently, you could only access state in class-based components. But with the advent of [hooks](https://reactjs.org/docs/hooks-intro.html) you can create stateful function components.
- Since this is a function, we don't have any methods, including `render()`. Just return your JSX as you would in the render function of a class-based component.
- `props.children` contains anything passed between the opening and closing tags: `<Button>I'm in children</Button>`

View File

@@ -1,6 +1,33 @@
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<div
class="codepen"
data-theme-id="36294"
data-prefill
data-editable="true"
data-height="100%"
data-theme-id="1"
data-default-tab="js,result"
>
<pre data-lang="html">
&lt;script src="https://unpkg.com/react@16/umd/react.development.js">&lt;/script>
&lt;script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js">&lt;/script>
&lt;div id='app'>&lt;/div>
</pre>
<pre data-lang="css">
.foo {
background: green;
}
</pre>
<pre data-lang="javascript">
ReactDOM.render(
&lt;div>Hello World&lt;/div>,
document.getElementById('app')
);
</pre>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
</body>
</html>

View File

@@ -1,12 +0,0 @@
import React from 'react';
export class App extends React.Component<any, any> {
render() {
let text = 'My App';
return (
<div className="App">
<h2>{text !== '' ? text : 'Default App Name'}</h2>
</div>
);
}
}

View File

@@ -1,15 +0,0 @@
.Button {
display: block;
background: #0078d4;
color: white;
padding: 5px 10px;
outline: none;
border: none;
}
.Button:hover {
background: #005a9e;
}
.Button:active {
background: #004578;
}

View File

@@ -1,10 +0,0 @@
import React from 'react';
import './Button.css';
export const Button = props => {
return (
<button className="Button" onClick={props.onClick}>
{props.children}
</button>
);
};

View File

@@ -1,7 +0,0 @@
import React from 'react';
export class Counter extends React.Component<any, any> {
render() {
return <p>hello</p>;
}
}

View File

@@ -1,4 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<p>hello world </p>, document.getElementById('app'));

262
step1-04/final/README.md Normal file
View File

@@ -0,0 +1,262 @@
# Step 4 - Introduction To React Demo
In this demo we'll be creating a simple counter that will display a count and increment on click.
Let's start this demo [in CodePen](https://codepen.io/micahgodbolt/pen/wOWeVb?editors=0010)
## React Hello World
```js
ReactDOM.render(<p>Hello World</p>, document.getElementById('app'));
```
- `ReactDOM.render()` - This function is how our code gets on the page. The function takes two parameters, the content to place on the page, and the location that you want it placed.
## Writing a React Component
A React component is a piece of code that returns a portion of your application. This can include HTML markup, CSS styles as well as JavaScript driven functionality.
Components can be created in two ways. The first is method is to use a [JavaScript class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes), which extends (inherits from) the `React.Component` class.
Classes in JavaScript provide a way to collect methods(functions) and properties(values) in an extendable container. We extend `React.Component` because it provides us with several built-in methods, including `render`.
```jsx
class App extends React.Component {
render() {
return <p>Hello World</p>;
}
}
```
We could also write this component as a function that takes in `props` and returns [JSX](https://reactjs.org/docs/introducing-jsx.html)
```jsx
const App = props => {
return <p>Hello World</p>;
};
```
Moving our "Hello World" markup into our App's `render` function, we can now update the `ReactDOM.render()` function to look like this:
```jsx
ReactDOM.render(<App />, document.getElementById('app'));
```
> Note that React components can be reused by writing them in the same way you would an HTML tag
### Props
Either way you write the component, the component can take in additional props using the same syntax as HTML attribute like `id` or `href`.
```jsx
<App text="Hello World" />
```
The prop can then be accessed by your component via `props.text` in our function or `this.props.text` in the class.
```jsx
const App = props => {
return <p>{props.text}</p>;
};
```
`props` allow your component to be more reusable as you can use multiple instances of the same component with different props.
```jsx
ReactDOM.render(
<div>
<App text="Hello World" />
<App text="How are you doing?" />
</div>,
document.getElementById('app')
);
```
> Note that a render function can only return a single element, so our two `App` components need to be wrapped in a `div`.
```jsx
const App = props => {
return <p>{props.text ? props.text : 'oops!'}</p>;
};
```
### Destructuring Props
Writing `props.text` over and over in a function (or `this.props.text` in a class) can be quite tedius. Since this is all JavaScript you could create a new variable for this text using variable assignment.
```jsx
const App = props => {
const text = props.text;
return <p>{text ? text : 'you missed something'}</p>;
};
```
This works fine for a single prop, but as your component starts to become more complex:
```jsx
<MyComponent
open={false}
count={5}
text="Hi there"
items={['cat', 'dog', 'bird']}
config={{
start: 1,
end: 10,
autoStart: true
}}
/>
```
> Note that all non-string values are passed through as JavaScript by wrapping them in `{}`.
Your code starts to look like this:
```jsx
const open = props.open;
const text = props.text;
const count = props.count;
const items = props.items;
const start = props.config.start;
const end = props.config.end;
```
A common approach to simplify this process is to use a technique called [`destructuring`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring).
Destructuring allows you to pull individual pieces of information out of a complex data type in a single line.
```jsx
const {
open,
text,
count,
items,
config: { start, end }
} = props;
```
So while this might be overkill right now, it makes it easier to add props down the road.
```jsx
const App = props => {
const text = props.text;
return <p>{text ? text : 'you missed something'}</p>;
};
```
### Cleanup
Before we move on, we'll reset our `ReactDom.render` to just return our App. This render function typically includes just the single component with no props.
Next we'll be creating a `Counter` component, so we'll add that to our App now, and start to write the control.
```jsx
const App = props => {
return <Counter text="chickens" />;
};
ReactDOM.render(<App />, document.getElementById('app'));
```
> Note the capitalization of `Counter`. HTML might not be case-sensitive, but JSX is! A common practice is to use the capitalized names of HTML elements to name corresponding React components: Button, Select, Label, Form, etc.
## React State
React allows each control to specify its own data store, called **state**. We can reference values in state when we render our UI, and we can also update state over the lifetime of our application.
> Most stateful components you'll see today will be `class` based. It is just recently possible to add state to function components through the use of [`hooks`](https://reactjs.org/docs/hooks-intro.html)
### Adding State
JavaScript classes uses a `constructor` method to instantiate each copy of a class, along with any applicable state. Let's create a new component called `Counter` and give it a state of `clicks` with a default value of `0`;
```js
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
clicks: 0
};
}
}
```
- The constructor takes in the component's `props`.
- The [`super(props)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super) function calls the constructor of the parent class (in this case `React.Component`).
- Our `counter` state value can now be accessed via `this.state.counter`. Later, we can update state by calling `this.setState({ counter: 1 })`.
### Creating our Counter
For our `Counter` component, the goal is to be able to track how many times the `counter`'s button is clicked. We'll use the following markup.
```jsx
render() {
const {text} = this.props;
const {clicks} = this.state;
return (
<div>
{text}: {clicks}
<button>Click</button>
</div>
)
}
```
### Writing our Button Click Handler
Our next step is to wire up the button to increment the `counter` in our component state.
> By convention we place other methods below `render()`, and private methods (those for internal use only) are prefixed with an underscore.
This function will update our component's state, incrementing the counter value by 1. (Note that `setState` only modifies the values of keys listed in the object passed as its parameter.)
```jsx
_onButtonClick = () => {
this.setState({
clicks: this.state.clicks + 1
});
};
```
> Note that this could also be written as `this.setState(prevState => ({ counter: prevState.counter + 1 }));` to ensure that state is not updated until the previous state has been determined
Now that we have a function to increment our count, all that's left is to connect it to our button.
```jsx
<button onClick={this._onButtonClick}>Click</button>
```
> Also note that each `Counter` maintains its own state! You can modify the state inside of one counter without affecting the others.
## Try it all out!
Add a couple `Counter`s to our `App`, each with different text. Notice how they can easy take in different props and maintain their own state.
## Moving this into out codebase
### Module Exports
### Module Imports
## Using a Button component
Buttons are among the most commonly written components. Custom buttons help abstract common styling, add icons or other decorations, and increase functionality (menu buttons etc). Let's take a quick look at a custom button component to see how it comes together.
```jsx
import React from 'react';
import './Button.css';
export const Button = props => {
return (
<button className="Button" onClick={props.onClick}>
{props.children}
</button>
);
};
```
- All components need to import React (don't worry, only one copy ever gets into your app)
- CSS files imported into the component are only loaded if the component is used
- React components can be created as a class **or** as a function. In this function component, props are passed in as a function parameter.
> Until recently, you could only access state in class-based components. But with the advent of [hooks](https://reactjs.org/docs/hooks-intro.html) you can create stateful function components.
- Since this is a function, we don't have any methods, including `render()`. Just return your JSX as you would in the render function of a class-based component.
- `props.children` contains anything passed between the opening and closing tags: `<Button>I'm in children</Button>`

View File

@@ -1,14 +1,11 @@
import React from 'react';
import { Counter } from './components/Counter';
export class App extends React.Component<any, any> {
render() {
return (
<div className="App">
<h2>My App</h2>
<Counter text="Chickens" />
<Counter text="Ducks" />
</div>
);
}
}
export const App = props => {
return (
<div>
<Counter text="chickens" />
<Counter text="ducks" />
</div>
);
};

View File

@@ -1,25 +1,26 @@
import React from 'react';
import { Button } from './Button';
export class Counter extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
counter: 0
clicks: 0
};
}
render() {
const { counter } = this.state;
const { text } = this.props;
const { clicks } = this.state;
return (
<div>
{text}: {counter}
<Button onClick={this._onButtonClick}>Click</Button>
{text}: {clicks}
<button onClick={this._onButtonClick}>Click</button>
</div>
);
}
_onButtonClick = () => {
this.setState((state) => ({ counter: state.counter + 1 }));
this.setState({
clicks: this.state.clicks + 1
});
};
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
ReactDOM.render(<App />, document.getElementById('app'));
ReactDOM.render(<p>hello world </p>, document.getElementById('app'));