minor readme tweaks

This commit is contained in:
Micah Godbolt
2019-02-27 20:25:28 -08:00
parent 3d69b2846d
commit 35eea2e60d
16 changed files with 62 additions and 42 deletions

View File

@@ -43,7 +43,7 @@
<div class="Tile-link">
React Intro
<div class="Tile-links">
<a target="_blank" href="https://github.com/Microsoft/frontend-bootcamp/tree/master/step1-04/demo">demo</a> |
<a target="_blank" href="./step1-04/demo/">demo</a> |
<a target="_blank" href="./step1-04/final/">final</a>
</div>
</div>

View File

@@ -1,14 +1,12 @@
# Introduction To React
# Introduction To React Demo
In our last example we saw how we could take a static HTML page and turn it into an interactive page with some buttons and their `onclick` handlers.
In this example we'll see how React turns that paradigm completely around. With React, the entire DOM is generated and maintained by JavaScript, directly inside the browser. This makes it easier to assemble your application out of reusable pieces, maintain state within a component, and pass data between them.
## Demo
In this demo we'll be creating a simple counter that will display a count and increment on click.
### The App
## The App
This is the starting point of our React application. It is a component just like the other ones we will be building, but this component will only ever be used once, to render the application. Here's how our app starts out. Let's walk through each line:
@@ -17,9 +15,10 @@ import React from 'react';
export class App extends React.Component {
render() {
const text = 'My App';
return (
<div>
<h2>My App</h2>
<div className="App">
<h2>{text != '' ? text : 'Default App Name'}</h2>
</div>
);
}
@@ -33,17 +32,16 @@ export class App extends React.Component {
- `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! If you declare `const name = 'Micah';` inside the render function, you can use that variable inside of your JSX like `<div>{name}</div>` or `<div><MyControl text={name} /></div>`. This works with function calls and some types of conditionals as well.
- You can use JavaScript inside of JSX!
### index.tsx
## index.tsx
This is the file that places your App onto the page.
> Note that to avoid build errors, this file has been renamed to `index.temp`. Change the name to `index.tsx`.
```ts
import React from 'react';
import ReactDOM from 'react-dom';
@@ -57,13 +55,13 @@ ReactDOM.render(<App />, document.getElementById('app'));
> This notation is called [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring), and it's awesome!
- `ReactDOM.render...` - This line calls the render function inside of `ReactDOM` and attaches our `<App />` component to the element with `id=app`. Take a peek in the index.html file. Shouldn't be too hard to find it.
### Counter Component
## 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. In the past JavaScript demo we might have accessed the counter element using `document.querySelector('.counter')` and manually incremented the number found there. While using the DOM as your data store works, it's REALLY hard to scale past the most basic demo.
React solves this by allowing 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
### 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`.
@@ -107,7 +105,7 @@ const { counter } = this.state;
const { text } = this.props;
```
#### Adding JSX
### Adding JSX
```jsx
return (
@@ -122,7 +120,7 @@ Each JSX return value needs to be a single element, so start with a wrapping `<d
Now let's see how we can use this component in our app.
#### Updating the App to Use Counters
### Updating the App to Use Counters
Before we can use our `Counter`, we need to import it into the App file.
@@ -144,7 +142,7 @@ return (
> 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
## 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.

View File

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

View File

@@ -2,8 +2,6 @@ import React from 'react';
export class Counter extends React.Component {
render() {
return (
);
return <p>hello</p>;
}
}

View File

@@ -4,7 +4,7 @@ import { Counter } from './components/Counter';
export class App extends React.Component {
render() {
return (
<div>
<div className="App">
<h2>My App</h2>
<Counter text="Chickens" />
<Counter text="Ducks" />

View File

@@ -3,7 +3,7 @@ import React from 'react';
export const TodoFooter = (props: any) => {
return (
<footer>
<p>footer</p>
<div>Footer</div>
</footer>
);
};

View File

@@ -2,6 +2,10 @@ import React from 'react';
export class TodoHeader extends React.Component {
render() {
return <div>Header</div>;
return (
<header>
<div>Header</div>
</header>
);
}
}

View File

@@ -2,6 +2,10 @@ import React from 'react';
export class TodoList extends React.Component<any, any> {
render() {
return <div />;
return (
<div>
<span>List</span>
</div>
);
}
}

View File

@@ -46,9 +46,11 @@ constructor(props) {
To avoid reaching into state over and over, we once again use destructuring to pull out the pieces we need.
```jsx
const { filter, todos } = this.state;
const { filter, todos = [] } = this.state;
```
> Note that I've set `todos` to default to an empty array so that the `todos` variable is never undefined
Now we can pass `filter` and `todos` into our components.
```jsx
@@ -99,7 +101,7 @@ In CSS-based styling, visual states are applied by adding and removing classes.
In traditional HTML forms, users interact with the form, and on submit, those values are captured and transmitted. Those are called **uncontrolled inputs**.
A **controlled input** is one whose value is defined by state, and interaction with that input updates state with each keystroke. This round trip process might sound inefficient, but in reality it has little to no impact, and it enables some advanced form functionality.
A **controlled input** is one whose value is defined by state, and interaction with that input updates state with each keystroke. This round trip process might sound inefficient, but in reality it has little to no impact, and it makes forms much easier to work with.
To create a controlled component, we need two things, which our demo already provides:
@@ -122,3 +124,5 @@ With those two pieces in place, we can update our uncontrolled input to being co
```jsx
<input value={this.state.labelInput} onChange={this._onChange} className="textfield" placeholder="add todo" />
```
> If you have React Dev Tools installed, open them up and take a look at labelInput as we type in the input.

View File

@@ -3,11 +3,11 @@ import { TodoListItem } from './TodoListItem';
export class TodoList extends React.Component<any, any> {
render() {
const { filter, todos } = this.props;
const { filter, todos = [] } = this.props;
// const filteredTodos = Object.keys(todos).filter(id => {
// return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
// });
const filteredTodos = Object.keys(todos).filter(id => {
return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
});
return (
<ul className="todos">
<TodoListItem />

View File

@@ -29,7 +29,7 @@ export class TodoApp extends React.Component<any, any> {
};
}
render() {
const { filter, todos } = this.state;
const { filter, todos = [] } = this.state;
return (
<div>
<TodoHeader filter={filter} />

View File

@@ -3,7 +3,7 @@ import { TodoListItem } from './TodoListItem';
export class TodoList extends React.Component<any, any> {
render() {
const { filter, todos } = this.props;
const { filter, todos = [] } = this.props;
const filteredTodos = Object.keys(todos).filter(id => {
return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);

View File

@@ -29,7 +29,7 @@ export class TodoApp extends React.Component<any, any> {
};
}
render() {
const { filter, todos } = this.state;
const { filter, todos = [] } = this.state;
return (
<div>
<TodoHeader filter={filter} />

View File

@@ -3,7 +3,7 @@ import { TodoListItem } from './TodoListItem';
export class TodoList extends React.Component<any, any> {
render() {
const { filter, todos } = this.props;
const { filter, todos = [] } = this.props;
const filteredTodos = Object.keys(todos).filter(id => {
return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);

View File

@@ -22,11 +22,11 @@ If you've ever used [Sass](https://sass-lang.com/) you are familiar with this co
Let's dive into the demo and see how TypeScript can help us better understand our component props, and guard against future regressions.
## Demo
# Demo
Let's start off in the TodoList, as that has the most data flow, up and down. There isn't any actionable UI in this component as we're simply passing `completed` down to each `TodoListItem`, but we can write a component interface to make sure that everything gets passed down properly.
### Writing TodoListProps
## Writing TodoListProps
Looking at our `TodoApp` we know that `TodoList` has three props, `filter`, `todos`, and `complete`. We'll start by creating an interface that represents this component's props called `TodoListProps`.
@@ -50,11 +50,11 @@ export class TodoList extends React.Component<TodoListProps, any>
Now that we have a typed component, let's go back to our `TodoApp` and see what happens if we try to change the name of a prop.
### Adding type safety
## Adding type safety
So far we've only established what our prop names are, not the values inside of them. Let's first look at `filter`, and see how we can improve that prop's type safety.
#### Filter Type
### Filter Type
We know that filter shouldn't be an object, array or function, so we can specify it should always be a string like this:
@@ -78,7 +78,7 @@ interface TodoListProps {
Now try going back to `TodoApp` and changing the `filter` attribute in `TodoList` to something else.
#### Complete Type
### Complete Type
The `complete` props isn't data, but rather a function. Fortunately, TypeScript can handle function types just as well as data.
@@ -92,7 +92,7 @@ interface TodoListProps {
For functions we are only concerned with the parameters passed in and the return value. You can see in the example above that the function takes in an `id` of type string, and returns `void`, which means it has no return.
### Todos Type
## Todos Type
The `todos` prop is interesting in that `todos` is an object with a bunch of unknown keys. So here's what that interface would look like.
@@ -113,7 +113,7 @@ interface TodoListProps {
Now that our interface is complete, try changing the word 'all' in `filter === all` and see that VS Code will tell you this condition will always be false. Imagine you had a typo in that line and you couldn't understand why your filter wasn't working.
### Abstracting types
## Abstracting types
Most of our components are going to need to add types for `todos` and `filter`, so it's a good thing that TypeScript allows us to abstract those. I've already written up and exported those shared types in the file `TodoApp.types.ts`, so we just need to import them and pull them into our interface.
@@ -127,7 +127,7 @@ interface TodoListProps {
}
```
### Updating TodoApp
## Updating TodoApp
Our `TodoApp` doesn't take any props, but it does have state. We can use TypeScript to define that as well.
@@ -139,7 +139,7 @@ export class TodoApp extends React.Component<{}, { todos: Todos; filter: FilterT
> Note that the first value in `<>` always refers to props. Since `TodoApp` takes none, we'll set it to an empty object.
### Writing TodoListItemProps
## Writing TodoListItemProps
Jumping down to the TodoListItem, as we start to write the TodoListItemProps we realize that two of the props, `label` and `completed` have already been defined in the `TodoItem` interface in `TodoApp.types`. So in the same way we can reuse individual types (`FilterTypes`), and extend upon entire interfaces.