fix(web): hoist hooks above early returns in TrialUrgencyBanner
useEffect and useCallback were declared after three `return null` guards, so when a user's subscription transitioned into the trial+≤7-days window React saw more hooks than the previous render and crashed with #310. Hoisted all hooks above the single guard return; render and dismiss behavior unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,71 +36,60 @@ export function TrialUrgencyBanner() {
|
||||
});
|
||||
|
||||
const subscription = subData?.subscription;
|
||||
const endDate = subscription?.endDate;
|
||||
const days =
|
||||
subscription?.status === "trial" && endDate
|
||||
? Math.max(
|
||||
0,
|
||||
Math.ceil((new Date(endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)),
|
||||
)
|
||||
: null;
|
||||
const visible = !isLoading && days !== null && days <= 7;
|
||||
const dismissKey = endDate ? getDismissKey(endDate) : null;
|
||||
|
||||
// Hydrate dismissal state from localStorage on mount
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
// Guard: not loaded yet, or no subscription
|
||||
if (isLoading || !subscription) return null;
|
||||
// Guard: not trial or no end date
|
||||
if (subscription.status !== "trial" || !subscription.endDate) return null;
|
||||
|
||||
const days = Math.max(
|
||||
0,
|
||||
Math.ceil((new Date(subscription.endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)),
|
||||
);
|
||||
|
||||
// Only show when 7 or fewer days remain
|
||||
if (days > 7) return null;
|
||||
|
||||
const dismissKey = getDismissKey(subscription.endDate);
|
||||
|
||||
// Check localStorage on mount and when endDate changes
|
||||
useEffect(() => {
|
||||
if (!mounted) return;
|
||||
if (!mounted || !dismissKey) return;
|
||||
const stored = localStorage.getItem(dismissKey);
|
||||
if (stored === "true") {
|
||||
setDismissed(true);
|
||||
}
|
||||
}, [mounted, dismissKey]);
|
||||
|
||||
// Fire trial_urgency_banner_viewed once when the banner becomes visible
|
||||
useEffect(() => {
|
||||
if (!subscription || subscription.status !== "trial" || !subscription.endDate) return;
|
||||
if (days > 7) return;
|
||||
if (dismissed) return;
|
||||
if (viewedRef.current) return;
|
||||
|
||||
if (!visible || dismissed || viewedRef.current || days === null) return;
|
||||
viewedRef.current = true;
|
||||
capture("trial_urgency_banner_viewed", {
|
||||
days_remaining: days,
|
||||
plan_name: subscription.plan?.name,
|
||||
billing_period: subscription.billingPeriod,
|
||||
plan_name: subscription?.plan?.name,
|
||||
billing_period: subscription?.billingPeriod,
|
||||
});
|
||||
}, [subscription, days, dismissed]);
|
||||
}, [visible, dismissed, days, subscription?.plan?.name, subscription?.billingPeriod]);
|
||||
|
||||
if (dismissed) return null;
|
||||
const handleCTAClick = useCallback(() => {
|
||||
if (days === null) return;
|
||||
capture("trial_urgency_banner_cta_clicked", {
|
||||
days_remaining: days,
|
||||
plan_name: subscription?.plan?.name,
|
||||
billing_period: subscription?.billingPeriod,
|
||||
});
|
||||
}, [days, subscription?.plan?.name, subscription?.billingPeriod]);
|
||||
|
||||
if (!visible || dismissed || days === null || !dismissKey) return null;
|
||||
|
||||
const handleDismiss = () => {
|
||||
capture("trial_urgency_banner_dismissed", {
|
||||
days_remaining: days,
|
||||
plan_name: subscription.plan?.name,
|
||||
billing_period: subscription.billingPeriod,
|
||||
plan_name: subscription?.plan?.name,
|
||||
billing_period: subscription?.billingPeriod,
|
||||
});
|
||||
localStorage.setItem(dismissKey, "true");
|
||||
setDismissed(true);
|
||||
};
|
||||
|
||||
const handleCTAClick = useCallback(() => {
|
||||
capture("trial_urgency_banner_cta_clicked", {
|
||||
days_remaining: days,
|
||||
plan_name: subscription.plan?.name,
|
||||
billing_period: subscription.billingPeriod,
|
||||
});
|
||||
}, [days, subscription.plan?.name, subscription.billingPeriod]);
|
||||
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
|
||||
Reference in New Issue
Block a user