第 6 章:条件与列表
学习目标
- 掌握 4 种条件渲染写法
- 熟练运用
map渲染列表 - 理解 key 的作用与最佳实践
- 避开条件/列表渲染的 4 个坑
一、条件渲染的 4 种写法
写法 1:if-else(早返回)
适合组件级别分支,早 return:
tsx
function Page({ status }: { status: 'loading' | 'ok' | 'empty' | 'error' }) {
if (status === 'loading') return <Spinner />;
if (status === 'error') return <ErrorPage />;
if (status === 'empty') return <Empty />;
return <Content />;
}优点:逻辑清晰,避免深层嵌套。
写法 2:三元运算符
适合有 else 分支的内联场景:
tsx
function Button({ disabled }: { disabled: boolean }) {
return (
<button disabled={disabled}>
{disabled ? '已禁用' : '点击'}
</button>
);
}写法 3:&& 短路
只关心 true 时渲染,不关心 false:
tsx
function Notice({ hasMsg }: { hasMsg: boolean }) {
return (
<div>
{hasMsg && <span>你有一条新消息</span>}
</div>
);
}⚠️ 坑 1:
count && <Badge />当count = 0会渲染数字 0!必须写count > 0 && <Badge />。
写法 4:switch
适合枚举多分支:
tsx
function StatusBadge({ s }: { s: 'ok' | 'warn' | 'err' }) {
switch (s) {
case 'ok': return <span className="ok">正常</span>;
case 'warn': return <span className="warn">警告</span>;
case 'err': return <span className="err">异常</span>;
}
}二、列表渲染基础
tsx
interface User { id: number; name: string; }
function UserList({ users }: { users: User[] }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
</li>
))}
</ul>
);
}必须给列表项设 key,否则 React 警告且性能差。
三、key 的关键作用
React 用 key 匹配前后两次渲染的元素:
| 场景 | 没 key | 有 key |
|---|---|---|
| 列表顺序变化 | 全部销毁重建 | 复用 DOM/状态 |
| 性能 | 差 | 快 |
| 组件 state | 错乱 | 正确 |
tsx
// ✅ 用稳定、唯一 ID
{items.map(item => <Item key={item.id} {...item} />)}
// ❌ 用 index(顺序变化就出问题)
{items.map((item, i) => <Item key={i} {...item} />)}⚠️ 坑 2:即使你看不到 bug,永远不要用 index 当 key,除非列表只读、永不重排、永不增删。
四、空列表与边界
tsx
function List({ items }: { items: string[] }) {
if (items.length === 0) return <Empty />; // ✅ 早返回
return <ul>{items.map(...)}</ul>;
}
// 或者
function List({ items }: { items: string[] }) {
return (
<>
{items.length === 0
? <Empty />
: <ul>{items.map(...)}</ul>}
</>
);
}五、嵌套列表
tsx
function GroupList({ groups }: { groups: Group[] }) {
return (
<div>
{groups.map(group => (
<section key={group.id}>
<h3>{group.name}</h3>
<ul>
{group.items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
</section>
))}
</div>
);
}⚠️ 坑 3:外层和内层的 key 必须都设,不能只设一层。
六、分页 / 切片
tsx
function PaginatedList({ items, page, pageSize }: Props) {
const start = (page - 1) * pageSize;
const visible = items.slice(start, start + pageSize);
return (
<>
{visible.map(item => <Card key={item.id} {...item} />)}
<Pagination total={items.length} pageSize={pageSize} />
</>
);
}七、过滤 / 搜索
tsx
function SearchableList({ items, keyword }: Props) {
const filtered = items.filter(item =>
item.name.toLowerCase().includes(keyword.toLowerCase())
);
if (filtered.length === 0) return <Empty text="无匹配结果" />;
return <ul>{filtered.map(...)}</ul>;
}八、本章小结
| 要点 | 关键 |
|---|---|
| 条件渲染 | if 早返 / 三元 / && / switch |
&& 陷阱 | 数字 0 会渲染,必须加判断 |
| 列表渲染 | array.map + 唯一 key |
| key 选择 | 稳定业务 ID,禁用 index |
| 空数据 | 早返回 <Empty /> |
| 嵌套列表 | 每层都要 key |
动手练习
- 状态徽章:根据
status显示「加载中/成功/失败/空」四种界面 - 学生列表:渲染 5 个学生,带搜索框过滤(实时)
- 分组列表:按
category分组的卡片,每组渲染该组项 - 找坑:以下代码点击按钮会发生什么?tsx
const [count, setCount] = useState(0); return <div>{count && <Badge count={count} />}</div>;
下一章:第 7 章:表单 →