第4课:列表渲染与条件渲染
第4课:列表渲染与条件渲染
学习目标
- 掌握列表渲染的进阶技巧
- 理解
key
属性的核心作用 - 灵活运用多种条件渲染方式
- 优化任务列表的交互体验
一、列表渲染优化实践
1. 复杂数据结构渲染
更新App.js
中的初始状态:
const [tasks, setTasks] = useState([
{
id: 1,
title: '学习React基础',
description: '完成组件和状态管理学习',
completed: false,
priority: 'high',
category: '学习'
},
{
id: 2,
title: '项目会议',
description: '下午3点团队进度同步',
completed: true,
priority: 'medium',
category: '工作'
}
]);
2. 增强型TaskCard组件
function TaskCard({ task, onToggle, onDelete }) {
const getPriorityColor = () => {
switch(task.priority) {
case 'high': return '#ff4444';
case 'medium': return '#ffbb33';
default: return '#00C851';
}
};
return (
<div className={`task-card ${task.completed ? 'completed' : ''}`}>
<div className="task-header">
<span
className="priority-dot"
style={{ backgroundColor: getPriorityColor() }}
/>
<h3>{task.title}</h3>
<span className="category-tag">{task.category}</span>
</div>
<p className="task-description">{task.description}</p>
<div className="task-footer">
<button
onClick={onToggle}
className={`status-btn ${task.completed ? 'completed' : ''}`}
>
{task.completed ? '✅ 已完成' : '⏳ 进行中'}
</button>
<button
onClick={onDelete}
className="delete-btn"
>
🗑️ 删除
</button>
</div>
</div>
);
}
3. Key的重要性示例
错误示例:
{tasks.map((task, index) => (
<TaskCard key={index} task={task} />
))}
正确实践:
{tasks.map(task => (
<TaskCard
key={task.id}
task={task}
onToggle={() => toggleTask(task.id)}
onDelete={() => deleteTask(task.id)}
/>
))}
为什么重要:
- 帮助React识别元素变化
- 维持组件状态稳定性
- 提升列表更新性能
二、条件渲染的四种方式
1. && 运算符
{!tasks.length && (
<div className="empty-state">
<img src="/empty.svg" alt="空状态" />
<p>暂时没有任务,添加一个吧!</p>
</div>
)}
2. 三元表达式
<div className="task-stats">
总计任务: {tasks.length}
{tasks.length > 0 ? (
<span>(已完成: {tasks.filter(t => t.completed).length})</span>
) : null}
</div>
3. 组件外返回
function TaskList({ tasks }) {
if (!tasks.length) {
return <EmptyState />;
}
return (
<div className="task-list">
{tasks.map(task => (
<TaskCard key={task.id} task={task} />
))}
</div>
);
}
4. 枚举对象
const priorityLabels = {
high: { text: '紧急', color: 'red' },
medium: { text: '一般', color: 'orange' },
low: { text: '低', color: 'green' }
};
// 在组件中使用
<span style={{ color: priorityLabels[task.priority].color }}>
{priorityLabels[task.priority].text}
</span>
三、实战:增强任务列表
1. 添加过滤功能
function App() {
const [filter, setFilter] = useState('all');
const filteredTasks = tasks.filter(task => {
if (filter === 'completed') return task.completed;
if (filter === 'active') return !task.completed;
return true;
});
return (
<div className="App">
<div className="filters">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => setFilter('all')}
>
全部 ({tasks.length})
</button>
<button
className={filter === 'active' ? 'active' : ''}
onClick={() => setFilter('active')}
>
未完成 ({tasks.filter(t => !t.completed).length})
</button>
<button
className={filter === 'completed' ? 'active' : ''}
onClick={() => setFilter('completed')}
>
已完成 ({tasks.filter(t => t.completed).length})
</button>
</div>
<TaskList tasks={filteredTasks} />
</div>
);
}
2. 样式优化 (App.css)
/* 过滤按钮 */
.filters {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.filters button {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 20px;
background: white;
cursor: pointer;
transition: all 0.3s;
}
.filters button.active {
background: #2196F3;
color: white;
border-color: #2196F3;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 40px;
opacity: 0.6;
}
.empty-state img {
width: 150px;
margin-bottom: 20px;
}
/* 任务卡片增强 */
.task-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.priority-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.category-tag {
background: #e0e0e0;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.8em;
}
.task-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
}
.delete-btn {
background: none;
border: none;
color: #ff4444;
cursor: pointer;
transition: transform 0.2s;
}
.delete-btn:hover {
transform: scale(1.1);
}
/* 动画效果 */
.task-card {
transition: transform 0.3s, box-shadow 0.3s;
}
.task-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
四、练习任务
-
实现分类过滤功能:
- 添加「工作」「学习」「生活」分类筛选
- 显示每个分类的任务数量
- 支持多选过滤
-
增强空状态提示:
- 不同过滤状态显示不同的提示文案
- 添加「新建任务」按钮到空状态区域
-
实现动画删除效果:
- 点击删除时添加渐出动画
- 使用CSS transition或react-transition-group
-
(挑战)添加排序功能:
- 支持按优先级排序
- 支持按创建时间排序
- 在卡片上显示创建时间
五、常见问题解答
Q: 为什么列表项会出现奇怪的重绘行为?
A: 检查:
- key是否稳定唯一
- 是否直接修改了状态数组
- 组件是否做了不必要的重新渲染
Q: 条件渲染导致布局跳动怎么办?
A: 解决方法:
- 使用CSS opacity过渡
- 保持DOM结构稳定(用空元素占位)
- 使用动画库处理过渡效果
Q: 如何处理复杂的条件组合?
A: 推荐方案:
- 提前计算渲染条件
- 使用策略模式封装条件判断
- 考虑使用useMemo优化计算
六、课程总结
本节重点:
- 列表渲染的key最佳实践
- 四种条件渲染模式的应用场景
- 复杂数据结构的可视化呈现
- 交互体验的全面优化
能力提升:
- 列表性能优化意识
- 条件逻辑的优雅处理
- 用户反馈的视觉设计
课后作业
- 完成分类过滤功能的实现
- 为任务添加创建时间字段并显示
- 尝试使用CSS Grid实现响应式任务列表布局
附加资源
- React列表渲染官方指南
- key的重要性深度解析
- CSS过渡动画教程
- react-transition-group文档
# 保存当前进度
git add .
git commit -m "feat: 完成列表渲染与条件渲染优化"