01-React基础入门——06-条件渲染
·

条件渲染
一、什么是条件渲染?
条件渲染是指根据不同的条件(如状态、属性、用户输入等)来决定渲染哪些 UI 元素。这是 React 中最常见的模式之一。
核心概念
- 使用 JavaScript 条件语句(
if、switch)或运算符(&&、? :)来决定渲染内容 - React 的声明式特性使得条件渲染非常直观
- 可以实现:登录/退出按钮、权限控制、加载状态、表单验证提示等
二、条件渲染的 5 种方式
2.1 if/else 语句
最适合逻辑复杂、代码块较长的场景。
function UserGreeting({ isLoggedIn, user }) {
if (isLoggedIn && user) {
return (
<div className="welcome">
<h1>欢迎回来,{user.name}!</h1>
<p>上次登录:{user.lastLogin}</p>
</div>
);
} else if (isLoggedIn) {
return <h1>欢迎回来!</h1>;
} else {
return (
<div className="login-prompt">
<h1>请先登录</h1>
<button>去登录</button>
</div>
);
}
}
2.2 三元运算符 (condition ? expr1 : expr2)
最适合简单的二分支场景,简洁直观。
function StatusBadge({ status }) {
return (
<span className={`badge ${status === 'active' ? 'badge-success' : 'badge-danger'}`}>
{status === 'active' ? '在线' : '离线'}
</span>
);
}
// 嵌套使用(谨慎,可读性会下降)
function ComplexStatus({ status }) {
return (
<div>
{status === 'loading' ? (
<Spinner />
) : status === 'error' ? (
<ErrorMessage />
) : (
<SuccessMessage />
)}
</div>
);
}
2.3 逻辑与运算符 (&&)
最适合"满足条件才渲染,否则什么都不渲染"的场景。
function NotificationBadge({ count }) {
return (
<div className="notification">
消息
{count > 0 && <span className="badge">{count}</span>}
</div>
);
}
function AdminPanel({ user }) {
return (
<div>
<h1>仪表盘</h1>
{user.isAdmin && <AdminTools />}
{user.canDelete && <DeleteButton />}
{user.canEdit && <EditButton />}
</div>
);
}
2.4 switch 语句
最适合多分支、每种分支逻辑较复杂的场景。
function OrderStatus({ status }) {
switch (status) {
case 'pending':
return (
<div className="status-pending">
⏳ 订单待处理
<button>取消订单</button>
</div>
);
case 'processing':
return (
<div className="status-processing">
🔄 订单处理中
<ProgressBar percent={50} />
</div>
);
case 'shipped':
return (
<div className="status-shipped">
📦 已发货
<TrackingNumber number="SF123456789" />
</div>
);
case 'delivered':
return (
<div className="status-delivered">
✅ 已送达
<ConfirmReceiptButton />
</div>
);
default:
return <div className="status-unknown">❓ 未知状态</div>;
}
}
2.5 枚举对象映射
最优雅的多分支方案,适合分支较多且逻辑简单的场景。
const STATUS_CONFIG = {
pending: {
icon: '⏳',
text: '待处理',
className: 'status-pending',
actions: ['cancel']
},
processing: {
icon: '🔄',
text: '处理中',
className: 'status-processing',
actions: []
},
completed: {
icon: '✅',
text: '已完成',
className: 'status-completed',
actions: ['review', 'reorder']
}
};
function OrderStatus({ status }) {
const config = STATUS_CONFIG[status] || STATUS_CONFIG.pending;
return (
<div className={config.className}>
<span>{config.icon}</span>
<span>{config.text}</span>
</div>
);
}
// 更高级的用法:直接映射到组件
const COMPONENT_MAP = {
loading: LoadingSpinner,
error: ErrorMessage,
success: SuccessMessage,
empty: EmptyState
};
function AsyncRenderer({ status, ...props }) {
const Component = COMPONENT_MAP[status];
return Component ? <Component {...props} /> : null;
}
三、阻止渲染(返回 null)
某些场景下,我们希望组件被调用但不渲染任何内容。
function AdBanner({ user, isVisible }) {
// VIP 用户不显示广告
if (user.isVIP) {
return null;
}
// 手动隐藏
if (!isVisible) {
return null;
}
return (
<div className="ad-banner">
限时优惠!点击购买
</div>
);
}
注意事项:
- 返回
null的组件仍然会执行生命周期(useEffect等) - 父组件的
useEffect不会因为子组件返回 null 而跳过 - 返回 null 的组件不会被渲染到 DOM 中
四、实战案例
4.1 登录/注册切换
function AuthPage() {
const [mode, setMode] = useState('login'); // 'login' | 'register' | 'forgot'
const renderForm = () => {
switch (mode) {
case 'login':
return <LoginForm onSwitch={setMode} />;
case 'register':
return <RegisterForm onSwitch={setMode} />;
case 'forgot':
return <ForgotPasswordForm onSwitch={setMode} />;
default:
return <LoginForm onSwitch={setMode} />;
}
};
return (
<div className="auth-container">
{renderForm()}
</div>
);
}
4.2 数据加载状态
function DataFetcher({ url }) {
const [state, setState] = useState({
status: 'idle', // 'idle' | 'loading' | 'success' | 'error'
data: null,
error: null
});
useEffect(() => {
setState({ status: 'loading', data: null, error: null });
fetch(url)
.then(res => res.json())
.then(data => setState({ status: 'success', data, error: null }))
.catch(error => setState({ status: 'error', data: null, error }));
}, [url]);
// 条件渲染的清晰写法
if (state.status === 'loading') {
return <LoadingSpinner />;
}
if (state.status === 'error') {
return <ErrorMessage error={state.error} />;
}
if (state.status === 'success' && !state.data) {
return <EmptyState />;
}
return <DataDisplay data={state.data} />;
}
4.3 权限控制
function PermissionGate({ user, requiredRole, children, fallback }) {
const hasPermission = () => {
const roleLevel = { admin: 3, editor: 2, viewer: 1 };
return (roleLevel[user.role] || 0) >= (roleLevel[requiredRole] || 0);
};
return hasPermission() ? children : (fallback || null);
}
// 使用
<PermissionGate user={currentUser} requiredRole="editor" fallback={<NoPermission />}>
<EditButton />
</PermissionGate>
4.4 响应式布局(根据屏幕尺寸)
function ResponsiveLayout() {
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
useEffect(() => {
const handleResize = () => setIsMobile(window.innerWidth < 768);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<div>
{isMobile ? (
<MobileNavigation />
) : (
<DesktopNavigation />
)}
{/* 只在桌面显示侧边栏 */}
{!isMobile && <Sidebar />}
<MainContent />
</div>
);
}
五、常见陷阱与最佳实践
5.1 陷阱:0 被渲染
// ❌ 错误:当 count = 0 时,会渲染数字 0
{count && <span>有 {count} 条消息</span>}
// ✅ 正确:明确的条件判断
{count > 0 && <span>有 {count} 条消息</span>}
// ✅ 或者使用三元运算符
{count ? <span>有 {count} 条消息</span> : null}
5.2 陷阱:字符串 ‘false’ 被渲染
// ❌ 错误:'false' 是真值,会渲染
{isVisible && <div>内容</div>} // 假设 isVisible = 'false'
// ✅ 正确:确保是布尔值
{!!isVisible && <div>内容</div>}
{Boolean(isVisible) && <div>内容</div>}
5.3 最佳实践:提取复杂逻辑
// ❌ 不推荐:JSX 中写复杂逻辑
function Component({ user, data, loading }) {
return (
<div>
{loading ? (
<Spinner />
) : user ? (
user.isVIP ? (
<VIPDashboard data={data} />
) : (
<RegularDashboard data={data} />
)
) : (
<LoginPrompt />
)}
</div>
);
}
// ✅ 推荐:提取为函数
function Component({ user, data, loading }) {
const renderContent = () => {
if (loading) return <Spinner />;
if (!user) return <LoginPrompt />;
if (user.isVIP) return <VIPDashboard data={data} />;
return <RegularDashboard data={data} />;
};
return <div>{renderContent()}</div>;
}
5.4 最佳实践:提前返回
// ✅ 提前返回让代码更清晰
function UserProfile({ user, loading, error }) {
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
if (!user) return <LoginPrompt />;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
六、性能优化
6.1 使用 React.memo 避免不必要的重渲染
const ExpensiveComponent = React.memo(({ data }) => {
// 复杂计算
return <div>{/* ... */}</div>;
});
function Parent({ showExpensive }) {
return (
<div>
{/* 条件渲染不影响 ExpensiveComponent 的 memo 优化 */}
{showExpensive && <ExpensiveComponent data={data} />}
</div>
);
}
6.2 懒加载条件组件
const LazyComponent = lazy(() => import('./HeavyComponent'));
function App({ showHeavy }) {
return (
<Suspense fallback={<div>加载中...</div>}>
{showHeavy && <LazyComponent />}
</Suspense>
);
}
七、练习题
基础题
- 实现一个
ToggleMessage组件:点击按钮切换显示/隐藏一段文字 - 实现一个
ScoreRating:根据分数(0-100)显示"优秀/良好/及格/不及格"
进阶题
- 实现一个
TabSwitcher:支持 3 个标签页,点击切换内容 - 实现一个
FormWizard:多步骤表单,根据当前步骤显示不同表单
参考答案
// 1. ToggleMessage
function ToggleMessage() {
const [visible, setVisible] = useState(false);
return (
<div>
<button onClick={() => setVisible(!visible)}>
{visible ? '隐藏' : '显示'}
</button>
{visible && <p>这是一段可切换显示的文字</p>}
</div>
);
}
// 2. ScoreRating
function ScoreRating({ score }) {
const getRating = () => {
if (score >= 90) return { text: '优秀', color: 'green' };
if (score >= 75) return { text: '良好', color: 'blue' };
if (score >= 60) return { text: '及格', color: 'orange' };
return { text: '不及格', color: 'red' };
};
const { text, color } = getRating();
return <span style={{ color }}>评分:{text} ({score}分)</span>;
}
// 3. TabSwitcher
function TabSwitcher() {
const [activeTab, setActiveTab] = useState(0);
const tabs = ['Tab 1', 'Tab 2', 'Tab 3'];
const contents = ['内容 1', '内容 2', '内容 3'];
return (
<div>
<div className="tabs">
{tabs.map((tab, index) => (
<button
key={index}
onClick={() => setActiveTab(index)}
style={{ fontWeight: activeTab === index ? 'bold' : 'normal' }}
>
{tab}
</button>
))}
</div>
<div className="tab-content">
{contents[activeTab]}
</div>
</div>
);
}
八、小结
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| if/else | 复杂逻辑、代码块长 | 清晰易读 | 不能内联 |
| 三元运算符 | 简单二分支 | 简洁 | 嵌套难读 |
| && 运算符 | 条件性显示 | 最简洁 | 注意 0 陷阱 |
| switch | 多分支 | 结构清晰 | 代码冗长 |
| 枚举映射 | 多分支、逻辑简单 | 最优雅 | 需要额外定义 |
核心要点:
- 根据场景选择合适的方式
- 避免 JSX 中嵌套过深的条件逻辑
- 提前返回让代码更清晰
- 注意数字 0 和字符串 ‘false’ 的陷阱
- 复杂逻辑提取为函数或单独组件
更多推荐


所有评论(0)