凌晨3点,我盯着自己写的代码陷入沉思——那些嵌套的三元表达式像藤蔓一样缠绕着整个组件,连我自己都看不懂了...
那是一个支付页面的代码片段:
{isPaid ? (
<section className="flex-1 flex flex-col xl:flex-row max-w-7xl mx-auto w-full p-4 sm:p-6 gap-6 xl:gap-8">
<div className="flex-1 flex flex-col gap-6">
<h2 className="text-lg font-semibold">Pagamento já realizado</h2>
<p className="text-sm text-gray-500">Obrigado por sua compra!</p>
<Button variant="outline" onClick={handleClick}>
Ver comprovante
</Button>
</div>
</section>
) : (
<>
{(isPixModalOpen && invoice?.pix) && <PixModal ... />}
<section className="flex-1 flex flex-col xl:flex-row max-w-7xl mx-auto w-full p-4 sm:p-6 gap-6 xl:gap-8">
<div className="flex-1 flex flex-col gap-6">
<PaymentMethodSelector />
<PaymentDetails />
<SecurityInfo />
</div>
<OrderSummary />
</section>
</>
)
当时的我:虽然有点丑,但能跑就行,对吧?直到在同一个文件里第四次看到这样的代码:
{isPaid ? (
<div>巨型JSX内容A</div>
) : (
<div>巨型JSX内容B</div>
)
那一刻我意识到——必须重构!
我的顿悟时刻
看着满屏的?和:,我想起了Inertia.js的<WhenVisible>组件。为什么不创造自己的守卫组件?
第一版:通用守卫
// 15行代码的救赎
function When({ condition, children }) {
return condition ? <>{children}</> : null;
}
// 使用示例
<When condition={isPaid}>
<PaymentSuccessView />
</When>
<When condition={!isPaid}>
<PaymentForm />
</When>
效果:代码缩进减少40%,可读性提升200%!
第二版:语义化组件
当支付逻辑扩展到5个页面时,我升级了方案:
// 支付专用守卫
function WhenPaid({ children }) {
const { paymentStatus } = usePayment();
return paymentStatus === 'PAID' ? <>{children}</> : null;
}
// 未支付守卫
function WhenNotPaid({ children }) {
const { paymentStatus } = usePayment();
return paymentStatus === 'PENDING' ? <>{children}</> : null;
}
// 优雅的使用方式
<WhenPaid>
<ReceiptView />
</WhenPaid>
<WhenNotPaid>
<PaymentOptions />
</WhenNotPaid>
守卫组件的威力:我的五大收获
-
代码即文档
看到<WhenAdmin>就知道这是管理员专属区域 -
告别括号地狱
再也不用数))}}的匹配关系 -
逻辑与UI分离
支付状态判断被封装在组件内部 -
测试变得简单
// 测试用例直击核心 test('WhenPaid shows children only when paid', () => { render(<WhenPaid><div data-testid="content"/></WhenPaid>); expect(screen.queryByTestId('content')).toBeNull(); mockPaymentStatus('PAID'); expect(screen.getByTestId('content')).toBeInTheDocument(); }); -
跨组件复用
同一个<WhenFeatureFlag>用在12个不同页面
我的进阶守卫模式
1. 智能包裹组件
解决"有时需要包裹div,有时不需要"的难题:
function ConditionalWrapper({ condition, wrapper, children }) {
return condition ? wrapper(children) : children;
}
// 使用示例
<ConditionalWrapper
condition={needsPadding}
wrapper={children => <div className="p-4">{children}</div>}
>
<UserProfile />
</ConditionalWrapper>
2. 权限守卫系统
// 权限守卫组件
function WhenRole({ role, children }) {
const { user } = useAuth();
return user.role === role ? <>{children}</> : null;
}
// 真实项目使用
<WhenRole role="ADMIN">
<DashboardAdminPanel />
</WhenRole>
<WhenRole role="EDITOR">
<ContentEditor />
</WhenRole>
3. 功能开关守卫
// 功能开关组件
function WhenFeature({ feature, children }) {
const { isEnabled } = useFeatureFlags();
return isEnabled(feature) ? <>{children}</> : null;
}
// 灰度发布控制
<WhenFeature feature="NEW_CHECKOUT_UI">
<CheckoutV2 />
</WhenFeature>
<WhenFeature feature="OLD_CHECKOUT_UI">
<CheckoutV1 />
</WhenFeature>
血泪教训:何时不用守卫组件
经过6个月实战,我总结出这些反模式:
适合三元表达式的情况:
// 简单条件 - 一目了然
{isLoading ? <Spinner /> : <Content />}
// 短路渲染 - 干净利落
{hasItems && <ItemList />}
// 文本微调 - 没必要组件化
<span>{error || 'No issues found'}</span>
守卫组件翻车现场:
// 过度抽象 - 更难懂了!
<When condition={!!(user && user.isAdmin && features.editMode)}>
<EditButton />
</When>
// 还不如直接写:
{user?.isAdmin && features.editMode && <EditButton />}
我的守卫组件实施法则
-
渐进式采用
从最痛的if-else开始重构,不要强求一步到位 -
命名即契约
组件名必须清晰表达业务逻辑:
✅<WhenSubscriptionActive>
❌<ConditionalWrapper3> -
团队共识优先
在代码评审时演示对比案例:| 指标 | 三元表达式 | 守卫组件 | 改进幅度 | |--------------|------------|----------|----------| | 可读性 | ★★☆ | ★★★★★ | +150% | | 可维护性 | ★★☆ | ★★★★☆ | +100% | | 逻辑复用 | 不可能 | 轻松复用 | ∞ | -
性能警戒线
复杂条件使用useMemo:function WhenComplex({ children }) { const shouldRender = useMemo(() => { // 复杂计算逻辑 }, [deps]); return shouldRender ? children : null; }
重构前后的心灵感悟
"以前我觉得代码能跑就行,现在明白代码是写给人看的——守卫组件就像给代码加上了路标,让后续维护者(包括三个月后的我自己)不再迷失在条件判断的迷宫中。"
最后送你的礼物:在我的个人工具箱里珍藏的守卫组件模板:
import React from 'react';
interface GuardProps {
children: React.ReactNode;
fallback?: React.ReactNode;
}
// 基础守卫模板
export const When = ({
condition,
children,
fallback = null
}: GuardProps & { condition: boolean }) => {
return condition ? <>{children}</> : <>{fallback}</>;
};
// 权限守卫
export const WhenRole = ({
role,
children,
fallback
}: GuardProps & { role: string }) => {
const { user } = useUser();
return (
<When condition={user.role === role} fallback={fallback}>
{children}
</When>
);
};
// 功能开关守卫
export const WhenFeature = ({
feature,
children,
fallback
}: GuardProps & { feature: string }) => {
const { isActive } = useFeatureToggle();
return (
<When condition={isActive(feature)} fallback={fallback}>
{children}
</When>
);
};
真理时刻:当我在代码评审中展示重构对比时,团队Leader说:"这就像把迷宫地图换成了GPS导航"。守卫组件不改变功能,但彻底改变了开发体验——这大概就是优雅代码的力量。