大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
综合练习与 Next.js 方向
这一课你将能做到
- 用 React 做一个「站点清单」:完成切换 + 保存中禁用
- 对照清单确认 props/state/列表/条件/事件都用到了
- 明确下一步进入
nextjs-essentials或补 JS 弱点
为什么要练
前面课是零件。零件不会自己变成「能点的一页」。
用一个小清单把 useState、不可变更新、map+key、条件、disabled 串起来。做完应有「我能独立写一块交互」的感觉,而不是只会背术语。
本站路径页就是「若干站点 + 状态」的放大版。练习刻意缩小,方便你抄进沙盒或临时页。
先建立图像
三项站点
├─ 切换 done(只改 state,新数组)
├─ 空列表 →「暂无站点」
└─ 「同步」假请求 500ms → 期间全禁用
跟着做
练习要求
做一个组件 StationList:
- 初始三项:
{ id, title, done } - 每项有按钮切换
done(文字「标记完成 / 未完成」即可) - 点「同步」模拟
await delay(500),期间按钮disabled,可显示「同步中」 done === true的标题加删除线或后缀 ✓- 零项时(自己清空测试)显示「暂无站点」
可选:用 Context 提供只读字符串 mode(如「准备去讲」)显示在顶部。
参考骨架(请自己补全切换与空态,不要只停留在看):
import { useState } from "react";
const INITIAL = [
{ id: "html", title: "HTML/CSS", done: false },
{ id: "js", title: "JavaScript", done: false },
{ id: "react", title: "React", done: false },
];
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function StationList() {
const [items, setItems] = useState(INITIAL);
const [syncing, setSyncing] = useState(false);
function toggle(id) {
setItems((prev) =>
prev.map((x) => (x.id === id ? { ...x, done: !x.done } : x)),
);
}
async function sync() {
if (syncing) return;
setSyncing(true);
try {
await delay(500);
} finally {
setSyncing(false);
}
}
if (items.length === 0) return <p>暂无站点</p>;
return (
<div>
<ul>
{items.map((x) => (
<li key={x.id}>
<span style={{ textDecoration: x.done ? "line-through" : "none" }}>
{x.title}
{x.done ? " ✓" : ""}
</span>
<button
type="button"
disabled={syncing}
onClick={() => toggle(x.id)}
>
{x.done ? "标为未完成" : "标为完成"}
</button>
</li>
))}
</ul>
<button type="button" disabled={syncing} onClick={sync}>
{syncing ? "同步中" : "同步"}
</button>
</div>
);
}
清空测试:临时 setItems([]) 或加一个「清空」按钮,确认空态出现。
验收清单
- 函数组件 + useState
- map + 稳定 key(用
id不是 index) - 更新数组用新数组,不写
items[i].done = true - 有 loading/disabled
- 无控制台报错
- 空列表有文案
你可能会懵的地方
-
还不会搭 Vite?
可在已有 Next/platform里建临时页;或用 react.dev 文档里的在线沙盒。先跑起来比纠结配置重要。 -
想直接改 DOM?
忍住。只改 state。 -
同步失败?
加 error 文案;本练习允许假成功。想加分就try/catch显示失败。 -
toggle 里为什么用
prev =>?
连续点、严格模式双调用时更稳,始终基于最新数组。 -
练习要上 TypeScript 吗?
建议:Props 与 LoadState 用 TS,和 typescript-essentials 收束。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
items[i].done = true | 可能不刷新 | map 出新数组 |
key={index} | 重排绑错 | key={x.id} |
| 同步中仍能狂点 | 重复请求 | syncing 锁 |
| 空列表空白 | 像坏了 | 「暂无站点」 |
| 做一个巨石 App.js | 难迁 Next | 按页面/组件拆文件 |
| 跳过验收清单 | 假完成 | 逐项勾 |
自测三问
- 切换完成时为什么要
{ ...x, done: !x.done }? - 若漏写 key 会怎样?
- 下一步你是补 JS、进 TypeScript,还是直接 Next?为什么?
练习加码
增加:空态文案、错误重试按钮、列表 key={slug}。
禁止:useEffect 无依赖刷屏、用 index 当 key 还删除中间项。
站内下一步
| 目标 | 打开 |
|---|---|
| App Router / 本站框架 | /tracks/nextjs-essentials |
| 类型 | /tracks/typescript-essentials |
| JS 不熟 | /tracks/javascript-core |
| 岗位走站 | /learn?role=frontend |
留一句
「React 里我最容易踩的坑是 ,我准备这样练:。」
小结一句话
用清单小练习把状态与列表渲染练熟;再学 Next,只是多了路由与工程约定。
动手加深
把作品对照 platform:路由、取数、登录门控各差在哪?差的三点就是 Next 轨学习目标。
跟着做(加厚 · react-practice)
步骤:最小可运行对照
在 Vite 或 Next 练习页粘贴本课核心示例,改一个输入,确认屏幕变化符合预期。再故意写一个错法表里的写法,看控制台/界面如何坏,然后改回。
每个概念都问:state 在谁手里?UI 哪一块会因它重绘?
操作顺序:打开对应文件 → 改一处可观察点(文案、类型、className 或分支)→ 保存看结果 → 复原或留下有意义的小改动。
步骤:错法故意练一次
先按「用 jQuery 式直接改 DOM 冒充完成」做 30 秒(或在注释里写出若这样做会怎样),再改成「改数据(state/props)让 UI 推导出来」。 目的不是留坑,而是让失败现象进长期记忆。
你可能会懵的地方(加厚)
-
热更新后状态丢了?
正常:整树重挂会重置 state。用 key 或外提状态保留;开发期别被吓到。 -
和文档示例不一致?
以你锁的 React 大版本 + react.dev 为准;旧博客的 class 组件示例仅作历史阅读。 -
这课和前后课怎么接?
回看大纲上一课的小结一句、下一课标题;用自己的话写「本课产出什么可观察结果」。写不出来就重做跟着做。
错法对照(加厚)
| 错法 | 问题 | 正解 |
|---|---|---|
| 用 jQuery 式直接改 DOM 冒充完成 | 假完成/埋雷 | 改数据(state/props)让 UI 推导出来 |
| 只看不敲 | 第二天全忘 | 至少跑通本课步骤 |
| 跳过自测三问 | 以为会了 | 闭卷答再对稿 |
延伸
- 精读 react.dev Learn
- 对照
platform/src/components读真实代码,指出哪一课的概念在用