← FC Coder · HomePhase 02 · Lesson 19 · 60 min
Lesson19
Phase Two · Fantasy Builder · Matchday 19 · Memory

useState
给组件加记忆

Today's 3 Jobs · 今天这三件事
  1. 01
    PlayerCard 顶部 import useState · 内部用
    const [x, setX] = useState(false)
  2. 02
    🌟 点卡切换金边 · 卡有记忆了
    Phase 2 最大跳跃
  3. 03
    22 张卡各自独立 state · 互不影响
    组件实例隔离

上节课卡能响应点击 —— 但 alert 弹完就没了。卡不知道自己被点过。今天 useState 进场 —— 卡有记忆了。点哈兰德 · 金边出现 · 再点取消。Phase 2 最大的跳跃 —— 从无记忆到有记忆。

Essence · state vs props

state = 组件自己的内部记忆

props 是外面传进来不可改 · state 是组件自己内部可改。

Chalk Board · 两种数据
const [isSelected, setIsSelected] = useState(false);
当前值 · 改它的函数 · 初始值
props
player / isSelected (外传)
state
useState(false) (内记)
props 不能 · state 必须用 setter

比喻:props 是身份证(外面给 · 不能改)· state 是教练标记(自己内部记 · 自己改)。

Essence · setter 是通知 · 不是直接改

教练告诉裁判 · 裁判才通知 · 才换

setIsSelected(true) ≠ isSelected = true。React 是裁判 · setter 是告诉它。

Chalk Board · 直接改 vs 通过 setter
onClick={() => setIsSelected(!isSelected)}
直接改 · ❌
isSelected = true · React 不知道 · 卡不变
setter · ✅
setIsSelected(true) · React 收到 · 重画
切换
setIsSelected(!isSelected)

setter 是异步的 —— 调用后下一轮渲染才生效。今天只看不深讲 · Phase 3 useEffect 时正经讲。

预览 · 试试点这张卡

本课页面里我直接放一张 · 你能切换金边

点它 · 看金边出现 / 消失 · 你也会在 sandbox 见到 22 张这样的。

哈兰德

ST · 评分 91

挪威 · 曼城

Roster · 今天 3 个新工具

useState · setter · !boolean

抄着用 · 看 22 张独立切换是今天的「哇」。

  1. 01
    const [x, setX] = useState(0)
    给组件加记忆 —— React 帮你记住 + 重画。
    解构 [当前值, 改它的函数]。括号里是初始值。
  2. 02
    setIsSelected(!isSelected)
    告诉 React 改 —— 不直接改 isSelected · 而是 setter。
    教练告诉裁判 · 裁判才通知。React 收到才重画。
  3. 03
    !isSelected
    反 boolean —— true → false · false → true · 切换。
    Phase 0 #3 boolean 见过 · 今天 ! 运算符进场。
Half 2 · 在屏幕上

给 PlayerCard 加记忆 · Phase 2 最大跳跃

做完一步就点 ✓。Step 5 那一下点卡切换 · 这是今天的「哇」。

01import + useState 进场
01Min

Cursor + pnpm dev

打开 components/player-card/index.tsx。
02Min

顶部 import { useState } from 'react'

在文件最上面加 import。保存 · 浏览器不变。
03Min

组件函数体里 const [isSelected, setIsSelected] = useState(false);

在 return 之前加。删掉 props 里的 isSelected(组件内部管了)· 去掉之前 isSelected 走 props 的写法。
04Min

borderClass 改用 state · article onClick 切换

const borderClass = isSelected ? '金边' : '普通边'; <article onClick={() => setIsSelected(!isSelected)} className={`... ${borderClass}`}>。保存。
02🌟 点卡切换 · 卡有记忆
05Min

🌟 sandbox 里点哈兰德卡 · 金边出现

去掉 .map 里之前传的 isSelected props · 改成 {players.map(p => <PlayerCard player={p} key={p.name} />)}。点哈兰德 · 金边 + ✅ 已选。再点 · 取消。切换。
0322 张各自独立
06Min

🌟 点不同卡 · 各自独立

点福登 · 福登金边 · 哈兰德保持。再点哈兰德 · 哈兰德取消 · 福登保持。22 张卡 · 22 块战术板 · 互不影响。
07Min

DevTools Console 摸 ! 运算符

!true → false · !false → true · !1 → false · !0 → true · !'' → true · !'hi' → false。! 把任何值反成 boolean。
04玩 · button + 多 state · 截图
08Min

顺嘴看 · setter 不立刻生效

onClick={() => { setIsSelected(!isSelected); console.log('刚才:', isSelected); }} · 打印的是点之前的值。setter 异步 · 下一轮渲染才生效。今天只看不深讲 · Phase 3 useEffect 时讲。
09Min

加 <button>取消</button> 单独 setIsSelected(false)

<button onClick={(e) => { e.stopPropagation(); setIsSelected(false); }} ...>取消</button>。stopPropagation 抄着用。一定取消 · 不切换。
10Min

试加第 2 个 state · tapCount

const [tapCount, setTapCount] = useState(0); article onClick 里加 setTapCount(tapCount + 1); JSX 加 <p>被点 {tapCount} 次</p>。多个 useState 各自独立。
11Min

📸 截图

存 2026-XX-XX-我的卡有记忆.png · Phase 2 第四张战利品。
Half Time · 中场 · 讲给爸爸听

4 题 · 重点:state vs props · setter 的为什么

第 3 题用「教练 vs 裁判」比喻最直接。

01什么是 state?和 props 差在哪?用「身份证 / 教练标记」比喻。Hint ↓

props 是外面传进来不可改(身份证)· state 是组件自己内部可改(教练标记)。

02useState 的形状?const [x, setX] = useState(0) 三个部分各做什么?Hint ↓

x 当前值 · setX 改它的函数 · 0 初始值。解构拿到两个。

03为什么不是 isSelected = true 而是 setIsSelected(true)?用「教练 vs 裁判」比喻。Hint ↓

教练告诉裁判 · 裁判通知。React 是裁判 · setter 是告诉它。直接改 React 不知道 · 不重画。

0422 张卡共享一份组件代码 · 状态各自独立 —— 这是什么力量?Hint ↓

组件实例独立 state。写一份组件代码 · React 调用 22 次 · 22 个独立 useState。

Player Rating · 本课温度计

给 Phase 2 最大跳跃打个分

说真话。useState 难吗 · 看到金边切换爽吗。爸爸会看到。

今天难度Difficulty
0
今天开心Fun
0
Final Whistle · 终场哨

你的卡有记忆了 —— React 帮你管 22 个状态。

还有 11 步没打勾。Step 5 第一次点卡切换那一下今天就过 —— 其他下次补。