Skip to content

Commit a4e487e

Browse files
shamishamiclaude
andcommitted
Add lab6: React introduction (components, props, useState, onClick)
- exercise/App.js: 5 fill-in-the-blank TODOs covering the core React concepts a beginner needs: JSX, components, props, useState, onClick - exercise/index.html: CDN setup (React 18 + Babel standalone) so no npm or build step is needed — just open with VS Code Live Server - exercise/style.css: pre-provided card and badge styles - ans/: completed answer key - PLAN.md: updated folder tree and lab table to include lab6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 63429f6 commit a4e487e

7 files changed

Lines changed: 425 additions & 2 deletions

File tree

PLAN.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
|---------|-------|-------------|
3434
| **Today (Session 1)** | Setup + HTML/CSS foundations + fill-in exercises | A styled portfolio page on GitHub Pages |
3535
| Session 2 | Customize content, add JavaScript interactions | Polished portfolio, own domain (optional) |
36-
| Session 3+ | React intro (if needed), extra sections | Production-ready portfolio |
36+
| Session 3+ | React intro (lab6) — components, props, state | Interactive React portfolio card |
3737

3838
---
3939

@@ -196,6 +196,16 @@ lab5/ ← Deploy
196196
│ ├── style.css (pre-provided, fully complete)
197197
│ └── script.js (2 TODOs — your name + university year)
198198
└── GUIDE.md (step-by-step GitHub Pages deployment)
199+
200+
lab6/ ← React Introduction
201+
├── exercise/
202+
│ ├── index.html (pre-provided — CDN React + Babel setup)
203+
│ ├── style.css (pre-provided — card & badge styles)
204+
│ └── App.js (5 TODOs — components, props, useState, onClick)
205+
└── ans/
206+
├── index.html
207+
├── style.css
208+
└── App.js (completed example)
199209
```
200210

201211
| Lab | File to edit | TODOs | Concepts |
@@ -205,6 +215,7 @@ lab5/ ← Deploy
205215
| lab3 | `exercise/script.js` | 5 core + 2 bonus | Variables, functions, DOM manipulation |
206216
| lab4 | `exercise/style.css` + `script.js` | 6 core + 2 bonus | Dark mode toggle, classList, localStorage |
207217
| lab5 | `exercise/index.html` + `script.js` | 10 | Personalize content, then deploy |
218+
| lab6 | `exercise/App.js` | 5 | React components, props, useState, onClick |
208219

209220
Each lab builds on the previous one. Each TODO requires at most one line — just fill and save, and Live Server updates instantly.
210221

@@ -239,7 +250,7 @@ Each lab builds on the previous one. Each TODO requires at most one line — jus
239250

240251
- Rebuild the portfolio from scratch using her own content and design
241252
- Add more sections (Projects, Skills with icons, Contact form)
242-
- Intro to React — only if she's curious and time allows
253+
- Lab 6: React intro — components, props, useState (no npm needed, uses CDN)
243254

244255
---
245256

lab6/ans/App.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* ANSWER KEY — Lab 6 */
2+
3+
// ── Component 1: SkillBadge ───────────────────────────────────────────────
4+
5+
function SkillBadge({ skill }) {
6+
return (
7+
<span className="badge">{skill}</span> // TODO 1 ✅ {skill} displays the prop
8+
);
9+
}
10+
11+
// ── Component 2: ProfileCard ──────────────────────────────────────────────
12+
13+
function ProfileCard({ name, bio }) {
14+
15+
const [likes, setLikes] = React.useState(0); // TODO 2 ✅ start at 0
16+
17+
const skills = ["HTML", "CSS", "JavaScript", "React"];
18+
19+
return (
20+
<div className="card">
21+
22+
<h2>{name}</h2> {/* TODO 3 ✅ {name} displays the name prop */}
23+
24+
<p className="bio">{bio}</p>
25+
26+
<div className="skills">
27+
{skills.map(skill => (
28+
<SkillBadge key={skill} skill={skill} />
29+
))}
30+
</div>
31+
32+
{/* TODO 4 ✅ () => setLikes(likes + 1) adds 1 each click */}
33+
<button className="like-btn" onClick={() => setLikes(likes + 1)}>
34+
❤️ {likes}
35+
</button>
36+
37+
</div>
38+
);
39+
}
40+
41+
// ── Component 3: App ──────────────────────────────────────────────────────
42+
43+
function App() {
44+
return (
45+
<div>
46+
<h1>Lab 6 — My First React App</h1>
47+
48+
{/* TODO 5 ✅ pass real name and bio as props */}
49+
<ProfileCard
50+
name="Sunney"
51+
bio="High school student from Taipei, passionate about design and technology."
52+
/>
53+
54+
</div>
55+
);
56+
}
57+
58+
// ── Render ─────────────────────────────────────────────────────────────────
59+
const root = ReactDOM.createRoot(document.getElementById("root"));
60+
root.render(<App />);

lab6/ans/index.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<!-- ANSWER KEY — Lab 6 -->
3+
<html lang="zh-Hant">
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Lab 6 — My First React App</title>
8+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
9+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
10+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
11+
<link rel="stylesheet" href="style.css">
12+
</head>
13+
<body>
14+
<div id="root"></div>
15+
<script type="text/babel" src="App.js"></script>
16+
</body>
17+
</html>

lab6/ans/style.css

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* Lab 6 — pre-provided styles, no changes needed */
2+
3+
* {
4+
box-sizing: border-box;
5+
margin: 0;
6+
padding: 0;
7+
}
8+
9+
body {
10+
font-family: 'Segoe UI', sans-serif;
11+
background: #f0f2f5;
12+
min-height: 100vh;
13+
display: flex;
14+
flex-direction: column;
15+
align-items: center;
16+
padding: 40px 20px;
17+
color: #333;
18+
}
19+
20+
h1 {
21+
margin-bottom: 32px;
22+
font-size: 1.5rem;
23+
color: #1a1a2e;
24+
}
25+
26+
/* ── Profile Card ─────────────────────────────── */
27+
.card {
28+
background: white;
29+
border-radius: 16px;
30+
padding: 36px 32px;
31+
width: 100%;
32+
max-width: 420px;
33+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
34+
text-align: center;
35+
}
36+
37+
.card h2 {
38+
font-size: 1.8rem;
39+
margin-bottom: 8px;
40+
color: #1a1a2e;
41+
}
42+
43+
.card .bio {
44+
color: #666;
45+
line-height: 1.6;
46+
margin-bottom: 24px;
47+
}
48+
49+
/* ── Skill Badges ─────────────────────────────── */
50+
.skills {
51+
display: flex;
52+
flex-wrap: wrap;
53+
gap: 8px;
54+
justify-content: center;
55+
margin-bottom: 28px;
56+
}
57+
58+
.badge {
59+
background: #e94560;
60+
color: white;
61+
padding: 4px 16px;
62+
border-radius: 20px;
63+
font-size: 0.85rem;
64+
}
65+
66+
/* ── Like Button ──────────────────────────────── */
67+
.like-btn {
68+
background: white;
69+
border: 2px solid #e94560;
70+
color: #e94560;
71+
padding: 10px 28px;
72+
border-radius: 24px;
73+
font-size: 1rem;
74+
cursor: pointer;
75+
transition: all 0.2s;
76+
}
77+
78+
.like-btn:hover {
79+
background: #e94560;
80+
color: white;
81+
}

lab6/exercise/App.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
Lab 6 — App.js
3+
==============
4+
React lets you build UIs with "components".
5+
A component is just a JavaScript FUNCTION that returns JSX.
6+
7+
Python vs React:
8+
9+
Python function React component
10+
───────────────────── ─────────────────────────────────
11+
def greet(name): function Greet({ name }) {
12+
return f"Hi {name}" return <p>Hi {name}</p>;
13+
}
14+
15+
JSX = HTML written inside JavaScript. It looks like HTML but lives in a .js file.
16+
Rule: use className instead of class (class is a reserved word in JS)
17+
18+
── How to read this file ────────────────────────────────────────────────
19+
Start at the BOTTOM where App() and root.render() are.
20+
→ App renders ProfileCard
21+
→ ProfileCard renders SkillBadge (once per skill)
22+
→ Each component returns JSX that becomes real HTML in the browser
23+
─────────────────────────────────────────────────────────────────────────
24+
*/
25+
26+
27+
// ── Component 1: SkillBadge ───────────────────────────────────────────────
28+
//
29+
// This component shows ONE skill pill badge.
30+
// { skill } is a "prop" — data passed in from the parent (ProfileCard below).
31+
//
32+
// Vanilla JS way: React way:
33+
// <span>HTML</span> <SkillBadge skill="HTML" />
34+
35+
function SkillBadge({ skill }) {
36+
return (
37+
// TODO 1: Replace ___ with {skill} to display the prop text inside the badge.
38+
// In JSX, use curly braces { } to insert any JavaScript value.
39+
// Example: <span className="badge">{someVariable}</span>
40+
<span className="badge">___</span>
41+
);
42+
}
43+
44+
45+
// ── Component 2: ProfileCard ──────────────────────────────────────────────
46+
//
47+
// This component shows the full card: name, bio, skill badges, and a like button.
48+
// It receives two props from App: { name } and { bio }.
49+
50+
function ProfileCard({ name, bio }) {
51+
52+
// TODO 2: Add the starting number inside React.useState( ___ ).
53+
// useState lets the component remember the like count across clicks.
54+
// Start it at 0.
55+
//
56+
// React.useState(0) returns an array: [currentValue, setterFunction]
57+
// const [likes, setLikes] = React.useState(0);
58+
// → likes = current number (starts at 0)
59+
// → setLikes = function that updates the number and re-renders
60+
const [likes, setLikes] = React.useState(___);
61+
62+
// These are the skill badges to display — already set up for you!
63+
const skills = ["HTML", "CSS", "JavaScript", "React"];
64+
65+
return (
66+
<div className="card">
67+
68+
{/* TODO 3: Show the name prop inside an <h2> tag.
69+
Hint: <h2>{name}</h2>
70+
(Replace ___ with the right JSX expression) */}
71+
<h2>___</h2>
72+
73+
<p className="bio">{bio}</p>
74+
75+
{/* Skills — already complete. Notice:
76+
.map() loops over the array and returns one SkillBadge per item.
77+
key= is required by React to track each item in a list. */}
78+
<div className="skills">
79+
{skills.map(skill => (
80+
<SkillBadge key={skill} skill={skill} />
81+
))}
82+
</div>
83+
84+
{/* TODO 4: Make the button increase likes by 1 when clicked.
85+
onClick in JSX works like addEventListener("click", ...) in vanilla JS.
86+
87+
Vanilla JS: button.addEventListener("click", function() { count++ })
88+
JSX: <button onClick={() => setLikes(likes + 1)}>
89+
90+
Replace ___ with the correct onClick handler.
91+
Hint: onClick={() => setLikes(likes + 1)} */}
92+
<button className="like-btn" onClick={___}>
93+
❤️ {likes}
94+
</button>
95+
96+
</div>
97+
);
98+
}
99+
100+
101+
// ── Component 3: App ──────────────────────────────────────────────────────
102+
//
103+
// App is the ROOT component — the starting point of the whole page.
104+
// It renders ProfileCard and passes YOUR info as props.
105+
106+
function App() {
107+
return (
108+
<div>
109+
<h1>Lab 6 — My First React App</h1>
110+
111+
{/* TODO 5: Replace both ___ with YOUR real name and a short one-sentence bio.
112+
Props work like HTML attributes: name="Sunney" bio="..."
113+
The ProfileCard component will receive them as { name } and { bio }. */}
114+
<ProfileCard
115+
name="___"
116+
bio="___"
117+
/>
118+
119+
</div>
120+
);
121+
}
122+
123+
124+
// ── Render ─────────────────────────────────────────────────────────────────
125+
// This is how React connects to your HTML.
126+
// It finds <div id="root"> in index.html and puts the App component inside it.
127+
const root = ReactDOM.createRoot(document.getElementById("root"));
128+
root.render(<App />);

lab6/exercise/index.html

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE html>
2+
<!--
3+
Lab 6 — My First React App
4+
============================
5+
目標 Goal:
6+
Build an interactive profile card using React components.
7+
Same portfolio content as before — but now built the "modern" way!
8+
9+
Key ideas:
10+
Component = a JavaScript function that returns HTML-like code (JSX)
11+
Props = data you pass INTO a component (like function arguments)
12+
useState = lets a component "remember" a changing value
13+
14+
⚠️ How to run this file:
15+
Open this folder in VS Code →
16+
right-click index.html → "Open with Live Server"
17+
18+
(Opening index.html directly from your file manager won't work
19+
because React + Babel needs a real web server.)
20+
-->
21+
<html lang="zh-Hant">
22+
<head>
23+
<meta charset="UTF-8">
24+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
25+
<title>Lab 6 — My First React App</title>
26+
27+
<!-- React 18 — loaded from the internet, no npm needed! -->
28+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
29+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
30+
31+
<!-- Babel — lets us write JSX (HTML inside JS) directly in the browser -->
32+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
33+
34+
<link rel="stylesheet" href="style.css">
35+
</head>
36+
<body>
37+
38+
<!-- React will build everything inside this single <div> -->
39+
<div id="root"></div>
40+
41+
<!-- type="text/babel" tells Babel to process App.js before running it -->
42+
<script type="text/babel" src="App.js"></script>
43+
44+
</body>
45+
</html>

0 commit comments

Comments
 (0)