Skip to content

Commit db9fe5f

Browse files
committed
[dashboard] Guard subscribeToStripe against multiple calls
1 parent 0e37cdc commit db9fe5f

File tree

1 file changed

+28
-5
lines changed

1 file changed

+28
-5
lines changed

components/dashboard/src/components/UsageBasedBillingConfig.tsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ interface Props {
3232
hideSubheading?: boolean;
3333
}
3434

35+
// Guard against multiple calls to subscripe (per page load)
36+
let didAlreadyCallSubscripe = false;
37+
3538
export default function UsageBasedBillingConfig({ attributionId, hideSubheading = false }: Props) {
36-
const location = useLocation();
3739
const currentOrg = useCurrentOrg().data;
3840
const attrId = attributionId ? AttributionId.parse(attributionId) : undefined;
3941
const [showUpdateLimitModal, setShowUpdateLimitModal] = useState<boolean>(false);
@@ -49,6 +51,10 @@ export default function UsageBasedBillingConfig({ attributionId, hideSubheading
4951
undefined,
5052
);
5153

54+
// Stripe-controlled parameters
55+
const location = useLocation();
56+
const [stripeParams, setStripeParams] = useState<{ setupIntentId?: string; redirectStatus?: string }>({});
57+
5258
const now = useMemo(() => dayjs().utc(true), []);
5359
const [billingCycleFrom, setBillingCycleFrom] = useState<dayjs.Dayjs>(now.startOf("month"));
5460
const [billingCycleTo, setBillingCycleTo] = useState<dayjs.Dayjs>(now.endOf("month"));
@@ -90,14 +96,30 @@ export default function UsageBasedBillingConfig({ attributionId, hideSubheading
9096
}, [attributionId, refreshSubscriptionDetails]);
9197

9298
useEffect(() => {
99+
const params = new URLSearchParams(location.search);
100+
setStripeParams({
101+
setupIntentId: params.get("setup_intent") || undefined,
102+
redirectStatus: params.get("redirect_status") || undefined,
103+
});
104+
}, [location.search]);
105+
106+
const subscribeToStripe = useCallback(() => {
93107
if (!attributionId) {
94108
return;
95109
}
96-
const params = new URLSearchParams(location.search);
97-
if (!params.get("setup_intent") || params.get("redirect_status") !== "succeeded") {
110+
const { setupIntentId, redirectStatus } = stripeParams;
111+
if (!setupIntentId || redirectStatus !== "succeeded") {
98112
return;
99113
}
100-
const setupIntentId = params.get("setup_intent")!;
114+
115+
// Guard against multiple execution following the pattern here: https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application
116+
if (didAlreadyCallSubscripe) {
117+
console.log("didAlreadyCallSubscripe, skipping this time.");
118+
return;
119+
}
120+
didAlreadyCallSubscripe = true;
121+
console.log("didAlreadyCallSubscripe false, first run.");
122+
101123
window.history.replaceState({}, "", location.pathname);
102124
(async () => {
103125
const pendingSubscription = { pendingSince: Date.now() };
@@ -136,7 +158,8 @@ export default function UsageBasedBillingConfig({ attributionId, hideSubheading
136158
);
137159
}
138160
})();
139-
}, [attrId?.kind, attributionId, currentOrg, location.pathname, location.search, refreshSubscriptionDetails]);
161+
}, [attrId?.kind, attributionId, currentOrg, location.pathname, stripeParams, refreshSubscriptionDetails]);
162+
useEffect(subscribeToStripe, [subscribeToStripe]); // Call every time the function changes, and guard in against re-entry in the function itself
140163

141164
const showSpinner = !attributionId || isLoadingStripeSubscription || !!pendingStripeSubscription;
142165
const showBalance = !showSpinner;

0 commit comments

Comments
 (0)