掌握动态渲染,让 UI 随数据变化
条件渲染
三元运算符
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? (
<h1>欢迎回来!</h1>
) : (
<h1>请登录</h1>
)}
</div>
);
}
&& 运算符
当条件为 false 时不渲染,为 true 时渲染内容:
function Notification({ unreadCount }) {
return (
<div>
<h1>消息</h1>
{unreadCount > 0 && (
<span>有 {unreadCount} 条未读消息</span>
)}
</div>
);
}
注意:如果
unreadCount为 0,会渲染false,这是安全的。
switch 语句
function StatusBadge({ status }) {
const getBadge = () => {
switch (status) {
case 'success':
return <span className="badge success">成功</span>;
case 'warning':
return <span className="badge warning">警告</span>;
case 'error':
return <span className="badge error">错误</span>;
default:
return <span className="badge">未知</span>;
}
};
return getBadge();
}
早期返回
function UserProfile({ user }) {
// 加载状态
if (!user) {
return <div>加载中...</div>;
}
// 空状态
if (user.posts.length === 0) {
return <div>暂无动态</div>;
}
// 正常渲染
return (
<div>
<h1>{user.name}</h1>
<PostsList posts={user.posts} />
</div>
);
}
变量存储
function LoginButton({ isLoggedIn, onLogin, onLogout }) {
let button;
if (isLoggedIn) {
button = <button onClick={onLogout}>登出</button>;
} else {
button = <button onClick={onLogin}>登录</button>;
}
return (
<div>
<h1>请登录</h1>
{button}
</div>
);
}
列表渲染
基本 map 渲染
function ItemList() {
const items = ['苹果', '香蕉', '橙子'];
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
对象数组渲染
function UserList() {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
{ id: 3, name: 'Charlie', email: 'charlie@example.com' }
];
return (
<ul>
{users.map(user => (
<li key={user.id}>
<strong>{user.name}</strong>
<span>{user.email}</span>
</li>
))}
</ul>
);
}
渲染组件列表
function PostList({ posts }) {
return (
<div>
{posts.map(post => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}
function PostCard({ post }) {
return (
<article>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
);
}
嵌套列表
function CategoryList({ categories }) {
return (
<div>
{categories.map(category => (
<div key={category.id} className="category">
<h3>{category.name}</h3>
<ul>
{category.items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
))}
</div>
);
}
列表的 key
为什么需要 key?
Key 帮助 React 识别哪些元素发生了变化,提高渲染性能。
// ❌ 没有 key
items.map(item => <li>{item.name}</li>);
// ✅ 有 key
items.map(item => <li key={item.id}>{item.name}</li>);
使用稳定的 ID
// ❌ 不推荐:使用数组索引
items.map((item, index) => <li key={index}>{item.name}</li>);
// ✅ 推荐:使用唯一 ID
items.map(item => <li key={item.id}>{item.name}</li>);
// ✅ 备选:使用索引(但有条件)
items.map((item, index) => <li key={index}>{item.name}</li>);
key 的作用域
function App() {
const [items, setItems] = useState([{ id: 1, name: 'A' }]);
return (
<div>
{/* 这里的 key 影响这个范围内的元素 */}
{items.map(item => (
<div key={item.id}>
<ChildComponent item={item} />
</div>
))}
</div>
);
}
综合示例:待办清单
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: '学习 React', completed: false },
{ id: 2, text: '完成项目', completed: true },
{ id: 3, text: '写博客', completed: false }
]);
const [inputValue, setInputValue] = useState('');
const addTodo = () => {
if (!inputValue.trim()) return;
setTodos([
...todos,
{
id: Date.now(),
text: inputValue,
completed: false
}
]);
setInputValue('');
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const activeTodos = todos.filter(todo => !todo.completed);
const completedTodos = todos.filter(todo => todo.completed);
return (
<div>
<h1>待办清单</h1>
{/* 添加输入框 */}
<div className="input-group">
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addTodo()}
placeholder="添加新任务..."
/>
<button onClick={addTodo}>添加</button>
</div>
{/* 进行中的任务 */}
<div className="section">
<h2>进行中 ({activeTodos.length})</h2>
{activeTodos.length === 0 ? (
<p className="empty">暂无任务</p>
) : (
<ul>
{activeTodos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
)}
</div>
{/* 已完成的任务 */}
<div className="section">
<h2>已完成 ({completedTodos.length})</h2>
{completedTodos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span className="completed">{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</div>
</div>
);
}
渲染的注意事项
1. 避免在 JSX 中直接使用对象
// ❌ 错误
<div>{ { name: 'test' } }</div>
// ✅ 正确
<div>{JSON.stringify({ name: 'test' })}</div>
2. 渲染数字
// ✅ 直接渲染数字
<p>数量: {count}</p>
// ✅ 渲染计算结果
<p>总价: {price * quantity}</p>
3. 渲染数组
const arr = [1, 2, 3];
// ✅ 渲染数组(自动调用 toString)
<p>{arr}</p> // 输出: 1,2,3
// ✅ 使用 join
<p>{arr.join(', ')}</p> // 输出: 1, 2, 3
总结
本章我们学习了:
- 条件渲染的多种方式(三元运算符、&&、switch、早期返回)
- 列表渲染的基本方法
- key 的重要性
- 综合示例:待办清单
下一章我们将学习 React Hooks 的核心——useState。