diff --git a/index.html b/index.html index 7cbf2ab..92e17b2 100644 --- a/index.html +++ b/index.html @@ -43,7 +43,7 @@ diff --git a/step1-04/demo/README.md b/step1-04/demo/README.md index 9b7b9a2..d170275 100644 --- a/step1-04/demo/README.md +++ b/step1-04/demo/README.md @@ -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 ( -
-

My App

+
+

{text != '' ? text : 'Default App Name'}

); } @@ -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: `
` - We can use custom HTML tags corresponding to the React components we create: `
hi
` - Controls can be self-closing: `
` -- 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 `
{name}
` or `
`. 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(, 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 `` 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 ` 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. diff --git a/step1-04/demo/src/App.tsx b/step1-04/demo/src/App.tsx index e69de29..1dbb2ec 100644 --- a/step1-04/demo/src/App.tsx +++ b/step1-04/demo/src/App.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export class App extends React.Component { + render() { + let text = 'My App'; + return ( +
+

{text !== '' ? text : 'Default App Name'}

+
+ ); + } +} diff --git a/step1-04/demo/src/components/Counter.tsx b/step1-04/demo/src/components/Counter.tsx index 038b5e1..f56d26e 100644 --- a/step1-04/demo/src/components/Counter.tsx +++ b/step1-04/demo/src/components/Counter.tsx @@ -2,8 +2,6 @@ import React from 'react'; export class Counter extends React.Component { render() { - return ( - - ); + return

hello

; } } diff --git a/step1-04/demo/src/index.temp b/step1-04/demo/src/index.tsx similarity index 100% rename from step1-04/demo/src/index.temp rename to step1-04/demo/src/index.tsx diff --git a/step1-04/final/src/App.tsx b/step1-04/final/src/App.tsx index f3dc2af..f6051f7 100644 --- a/step1-04/final/src/App.tsx +++ b/step1-04/final/src/App.tsx @@ -4,7 +4,7 @@ import { Counter } from './components/Counter'; export class App extends React.Component { render() { return ( -
+

My App

diff --git a/step1-05/demo/src/components/TodoFooter.tsx b/step1-05/demo/src/components/TodoFooter.tsx index 9b089ac..a3044c1 100644 --- a/step1-05/demo/src/components/TodoFooter.tsx +++ b/step1-05/demo/src/components/TodoFooter.tsx @@ -3,7 +3,7 @@ import React from 'react'; export const TodoFooter = (props: any) => { return (
-

footer

+
Footer
); }; diff --git a/step1-05/demo/src/components/TodoHeader.tsx b/step1-05/demo/src/components/TodoHeader.tsx index 1f7802f..7f32901 100644 --- a/step1-05/demo/src/components/TodoHeader.tsx +++ b/step1-05/demo/src/components/TodoHeader.tsx @@ -2,6 +2,10 @@ import React from 'react'; export class TodoHeader extends React.Component { render() { - return
Header
; + return ( +
+
Header
+
+ ); } } diff --git a/step1-05/demo/src/components/TodoList.tsx b/step1-05/demo/src/components/TodoList.tsx index a26a8e5..1afff2b 100644 --- a/step1-05/demo/src/components/TodoList.tsx +++ b/step1-05/demo/src/components/TodoList.tsx @@ -2,6 +2,10 @@ import React from 'react'; export class TodoList extends React.Component { render() { - return
; + return ( +
+ List +
+ ); } } diff --git a/step1-06/demo/README.md b/step1-06/demo/README.md index 55934b2..7a61d01 100644 --- a/step1-06/demo/README.md +++ b/step1-06/demo/README.md @@ -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 ``` + +> If you have React Dev Tools installed, open them up and take a look at labelInput as we type in the input. diff --git a/step1-06/demo/src/components/TodoList.tsx b/step1-06/demo/src/components/TodoList.tsx index 9f12d6d..6322b8a 100644 --- a/step1-06/demo/src/components/TodoList.tsx +++ b/step1-06/demo/src/components/TodoList.tsx @@ -3,11 +3,11 @@ import { TodoListItem } from './TodoListItem'; export class TodoList extends React.Component { 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 (
    diff --git a/step1-06/exercise/src/TodoApp.tsx b/step1-06/exercise/src/TodoApp.tsx index b0dbfba..579d757 100644 --- a/step1-06/exercise/src/TodoApp.tsx +++ b/step1-06/exercise/src/TodoApp.tsx @@ -29,7 +29,7 @@ export class TodoApp extends React.Component { }; } render() { - const { filter, todos } = this.state; + const { filter, todos = [] } = this.state; return (
    diff --git a/step1-06/exercise/src/components/TodoList.tsx b/step1-06/exercise/src/components/TodoList.tsx index 9f605f5..4173d78 100644 --- a/step1-06/exercise/src/components/TodoList.tsx +++ b/step1-06/exercise/src/components/TodoList.tsx @@ -3,7 +3,7 @@ import { TodoListItem } from './TodoListItem'; export class TodoList extends React.Component { 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); diff --git a/step1-06/final/src/TodoApp.tsx b/step1-06/final/src/TodoApp.tsx index b0dbfba..579d757 100644 --- a/step1-06/final/src/TodoApp.tsx +++ b/step1-06/final/src/TodoApp.tsx @@ -29,7 +29,7 @@ export class TodoApp extends React.Component { }; } render() { - const { filter, todos } = this.state; + const { filter, todos = [] } = this.state; return (
    diff --git a/step1-06/final/src/components/TodoList.tsx b/step1-06/final/src/components/TodoList.tsx index 9f605f5..4173d78 100644 --- a/step1-06/final/src/components/TodoList.tsx +++ b/step1-06/final/src/components/TodoList.tsx @@ -3,7 +3,7 @@ import { TodoListItem } from './TodoListItem'; export class TodoList extends React.Component { 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); diff --git a/step1-07/demo/README.md b/step1-07/demo/README.md index eddeaf3..c468405 100644 --- a/step1-07/demo/README.md +++ b/step1-07/demo/README.md @@ -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 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.