Skip to content

Commit 2096e81

Browse files
authored
Merge pull request #1469 from vegetabill/bill/react-part-4-apps
React Part 4 - initial draft version for feedback
2 parents 8125405 + 9895eda commit 2096e81

File tree

3 files changed

+349
-3
lines changed

3 files changed

+349
-3
lines changed

README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@ Participants should memorize [common JavaScript built-in functions](/javascript/
173173
### Week 8 - Eventonica with React
174174

175175
1. [Eventonica Project](/projects/eventonica/eventonica-project.md)
176-
1. [React Lessons](/react-js)
177-
1. [Eventonica with React](/projects/eventonica/eventonica-part3-react.md)
176+
1. [All React Lessons](/react-js)
178177

179178
### Week 9 - PERN Mini-Project (Postgres+Express+React+Node)
180179

react-js/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ React is a very popular framework for building web apps.
55
- [Part 1 - Intro to React](./react-part-1-intro.md)
66
- [Part 2 - Component State](./react-part-2-component-state.md)
77
- [Part 3 - Components & Hierarchies](./react-part-3-component-hierarchies.md)
8-
- Part 4 - Create React App - [Open Issue #1337](https://github.com/Techtonica/curriculum/issues/1337)
8+
- [Part 4 - Full Apps](./react-part4-full-apps.md)
99
- Part 5 - App Architecture - [Open Issue #1338](https://github.com/Techtonica/curriculum/issues/1338)
1010

1111
### Feedback Wanted

react-js/react-part4-full-apps.md

+347
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
# React Part 4 - Full Apps using `create-react-app` (CRA)
2+
3+
## Projected Time
4+
5+
- 6 hours
6+
7+
### Prerequisites
8+
9+
- [React Part 3 - Components & Hierarchies](./react-part-3-component-hierarchies.md)
10+
11+
### Motivation
12+
13+
Until now, we've been using simple React development setups good for learning. But real apps usually need a bit more features available, so for your eventual final project you'll want to use `create-react-app` (which we'll call CRA in this lesson), a tool that generates a template empty React project so you can just starting writing your own code and not worry about how to setup all the moving parts.
14+
15+
#### Benefits
16+
17+
- **👀 Live reload!** When you save a change to a file, it will almost instantly update in your browser. No more need to hit Cmd+R to reload the page
18+
- You can organize your React components and CSS any way you want and consistently use `import` to refer to them (no more confusion about `require` vs `import`)
19+
- Quickly add npm packages using `yarn add`
20+
- Easily deploy to the cloud. You can use the `build` command to package all your code up
21+
- Lots of help available on Stack Overflow. Because many users, especially beginner engineers, are using CRA, they have probably encountered the same problem as you and are posting about it!
22+
23+
### Objectives
24+
25+
The important areas you should leave this lesson knowing.
26+
27+
- How does `create-react-app` compare to what we've been using so far?
28+
- The basic files generated and what each one does, e.g. index.html, App.js, App.css
29+
- How to add an npm package to an existing CRA app
30+
31+
### Lesson
32+
33+
Its official tagline is:
34+
35+
> Set up a modern web app by running one command
36+
37+
CRA is an officially supported command-line tool (CLI) that lets you create a new, empty React application that has many add-ons automatically set up for you so you can just begin coding.
38+
39+
```sh
40+
create-react-app my-fun-app
41+
```
42+
43+
Since the main benefit of CRA is that you don't need to worry about how it works, we're mainly going to focus on trying it out through guided practice but there are links below if you want to know more about how it works.
44+
45+
### Common Mistakes & Misconceptions
46+
47+
- CRA is only for toy apps and _real engineers_ setup React from scratch
48+
- Actually, many professional projects leverage CRA or another starting point template like [Next.js](https://nextjs.org/) when beginning applications. Even if you want to customize the app in some ways, the general setup is usually a good starting point for many types of projects and **it will not make your portfolio project seem amateur to use it**
49+
- You **must** use CRA to build React apps
50+
- Not true, but it is strongly recommended you build experience using it first before trying to do it yourself without CRA. Although it's much easier, especially for a beginner, many companies have ofted to create a customized setup so they have full control over all aspects of the application
51+
52+
## Guided Practice
53+
54+
Let's setup a small app.
55+
56+
- `[npx](https://bambielli.com/til/2018-10-06-npx/) create-react-app todo-cra`
57+
- _grab a coffee while it installs everything_
58+
- `cd todo-cra`
59+
- `yarn start`
60+
61+
The app should open in a new browser tab automatically and show a spinning atom logo.
62+
63+
- `code .` to open the generated project in your editor
64+
- Find `App.js` in the `src` folder
65+
- Change the contents to: `<p>Hello Techtonica!</p>` and save
66+
- The browser should live update automatically 🥳
67+
68+
### Folder Structure
69+
70+
Review the [existing files](https://create-react-app.dev/docs/folder-structure) of the project.
71+
72+
The most important files are
73+
74+
1. `src/App.css` - all the styles for your React components
75+
1. `public/index.html` - the non-React content of your web pages, e.g. favicon, static footer, etc
76+
1. `src/App.js` - mounts your root component
77+
78+
#### `App.css`
79+
80+
Change the color of the "Learn React" link to `#b36ff6` or another color you like on https://colors.lol/. Changes to CSS will live update as well.
81+
82+
#### `index.html`
83+
84+
- Change the page title to "TODO"
85+
- Find a nice favicon on https://www.flaticon.com/
86+
- Make this the new favicon
87+
88+
Now let's add the main application logic to `App.js`. We'll keep everything in one file for now but as your app grows, you probably want to split components to their own files.
89+
90+
### Starter Components
91+
92+
Create a new file.
93+
94+
```jsx
95+
// Todo.js
96+
import React from 'react';
97+
98+
class Todo extends React.Component {
99+
render() {
100+
const { todo } = this.props;
101+
return <div>{todo.text}</div>;
102+
}
103+
}
104+
export default Todo;
105+
```
106+
107+
```jsx
108+
// App.js
109+
import React from 'react';
110+
import Todo from './Todo';
111+
import './App.css';
112+
113+
class Todo extends React.Component {
114+
render() {
115+
const { todo } = this.props;
116+
return <div>{todo.text}</div>;
117+
}
118+
}
119+
120+
class App extends React.Component {
121+
constructor(props) {
122+
super(props);
123+
this.state = {
124+
todos: [{ text: 'Walk dog' }, { text: 'Feed cat' }]
125+
};
126+
}
127+
render() {
128+
const { todos } = this.state;
129+
return (
130+
<div className="App">
131+
<h1>Todos</h1>
132+
<div>
133+
{todos.length &&
134+
todos.map((todo, idx) => <Todo key={idx} todo={todo} />)}
135+
</div>
136+
</div>
137+
);
138+
}
139+
}
140+
141+
export default App;
142+
```
143+
144+
You should see the heading and a single todo.
145+
146+
#### Adding isCompleted
147+
148+
Since this is a task app we want to mark tasks as complete. So let's change the shape of the todo data to include an `isCompleted` boolean.
149+
150+
```js
151+
state = {
152+
todos: [
153+
{ text: 'Walk dog', isCompleted: false },
154+
{ text: 'Feed cat', isCompleted: false }
155+
]
156+
};
157+
```
158+
159+
Now let's update the `<Todo/>` component to render `isCompleted` as a checkbox. For attributes see [MDN Checkbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox).
160+
161+
```jsx
162+
class Todo extends React.Component {
163+
render() {
164+
const { todo } = this.props;
165+
const { text, isCompleted } = todo;
166+
return (
167+
<div>
168+
<input type="checkbox" checked={isCompleted}></input>
169+
{text}
170+
</div>
171+
);
172+
}
173+
}
174+
```
175+
176+
Manually edit the data and add some completed and incompleted tasks to test.
177+
178+
#### Checkbox
179+
180+
Now let's allow the user to change `isCompleted` by checking the checkbox. In React, input elements have an `onChange` prop that accepts a function called when the user modifies the input. See [handling events](https://reactjs.org/docs/handling-events.html) in the official docs.
181+
182+
```jsx
183+
class Todo extends React.Component {
184+
toggleCompletion(todo) {
185+
todo.isCompleted = !todo.isCompleted;
186+
console.log('todo =>', todo);
187+
}
188+
189+
render() {
190+
const { todo } = this.props;
191+
const { text, isCompleted } = todo;
192+
return (
193+
<div>
194+
<input
195+
type="checkbox"
196+
checked={isCompleted}
197+
onChange={this.toggleCompletion.bind(this, todo)}
198+
></input>
199+
{text}
200+
</div>
201+
);
202+
}
203+
}
204+
```
205+
206+
Click the checkbox a few times. Nothing seems to happen. But in the log the code is working:
207+
208+
```
209+
todo => Object { text: "Walk dog", isCompleted: true }
210+
todo => Object { text: "Walk dog", isCompleted: false }
211+
todo => Object { text: "Walk dog", isCompleted: true }
212+
```
213+
214+
This is because, as you probably remember, **React only re-renders when state changes**. It assumes props are read-only and do not change, even though as the above shows, you can modify them since JavaScript doesn't stop you. So we need to change the state, but state lives one level above `Todo` in `App`. Like we learned in Part 3, passing functions down to child components is a good strategy for handling this situation.
215+
216+
#### Create an Updater Function in App
217+
218+
```jsx
219+
// New method
220+
updateTodo(todo, changes) {
221+
this.setState({
222+
todos: this.state.todos.map((existing) => {
223+
if (todo === existing) {
224+
return { ...existing, ...changes };
225+
}
226+
return existing;
227+
}),
228+
});
229+
}
230+
// ...
231+
// pass the method in as a prop, bound to this so it will work
232+
todos.map((todo, idx) => (
233+
<Todo
234+
key={idx}
235+
todo={todo}
236+
updateTodo={this.updateTodo.bind(this)}
237+
/>
238+
))
239+
```
240+
241+
This code is a little more complicated than you might've expected. This is because it's creating a whole new copy of the array (using `.map`) rather than finding the item and changing its data.
242+
243+
#### Rewrite `updateTodo` in your own coding style
244+
245+
You'll learn a lot more about different ways of managing state in React, but that is a whole big topic in itself. For now, you should rewrite the `updateTodo` function in a way that makes sense for you. There are many ways to accomplish the same logic.
246+
247+
Now test out clicking the checkbox. It should toggle correctly as expected.
248+
249+
### Independent Practice
250+
251+
With your daily pair, review each other's code from above and see if you can explain how each part is working, especially how you chose to rewrite the `updateTodo` function. Add some `console.log` statements to verify your understanding.
252+
253+
Next, pair program to add a feature: sorting.
254+
255+
#### Sorting
256+
257+
First we need something to sort by so let's add a `createdAt` property to each item that will be a `Date` object.
258+
259+
```js
260+
{
261+
text: "Walk dog",
262+
isCompleted: false,
263+
createdAt: new Date(),
264+
},
265+
```
266+
267+
Let's support two sort options: newest first or oldest first.
268+
269+
Let's make newest first the default and add it to our starting state.
270+
271+
```js
272+
state = {
273+
sort: 'oldest',
274+
todos: []
275+
};
276+
```
277+
278+
Then based on that value, we'll sort `state.todos`.
279+
280+
```js
281+
const { todos, sort } = this.state;
282+
if (sort === 'oldest first') {
283+
todos.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
284+
}
285+
```
286+
287+
Check the browser and make sure it's sorting correctly. You may have to give the todos specific dates to test it, e.g. `new Date("2021-01-15 00:00:00-0800")`
288+
289+
Now add another option to sort by `'newest'`.
290+
291+
```jsx
292+
if (sort === 'oldest') {
293+
todos.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
294+
} else if (sort === 'newest') {
295+
todos.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
296+
}
297+
```
298+
299+
Edit the starting state and make sure the sorting works as expected.
300+
301+
#### Sorting Toggle
302+
303+
Now add a button that switches the sorting from one option to the other.
304+
305+
```jsx
306+
<div>
307+
<button
308+
onClick={() =>
309+
this.setState({ sort: sort === 'oldest' ? 'newest' : 'oldest' })
310+
}
311+
>
312+
⬇️ Sort by {sort === 'oldest' ? 'newest' : 'oldest'} first
313+
</button>
314+
</div>
315+
```
316+
317+
Test out the button to make sure it works correctly.
318+
319+
### Challenges
320+
321+
#### Style the Todo Items
322+
323+
If they are complete, style them to indicate that, perhaps by graying them out or using strikethrough.
324+
325+
#### Add Input Form
326+
327+
To create new todos, add an input form that will create a new todo item and add it to the state so it shows up in the list.
328+
329+
#### Add `reactstrap` npm package to your CRA project
330+
331+
If you haven't yet reviewed it, review the lesson on [React Styling](./styling-react.md).
332+
333+
Let's use some prebuilt and prestyled components to make our Todo App look snazzier 😎
334+
335+
Since it has such good docs, there is an official page to do this:
336+
337+
https://create-react-app.dev/docs/adding-bootstrap/
338+
339+
Try it out and see what you can do!
340+
341+
### Deploy your Todo App using GitHub Pages Hosting
342+
343+
https://github.com/gitname/react-gh-pages
344+
345+
### Supplemental Materials
346+
347+
- Official `create-react-app` docs: https://create-react-app.dev/

0 commit comments

Comments
 (0)