mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 10:27:49 +00:00
fix(telegram-redirect): cancel pending timers on effect re-run/unmount
TelegramRedirect schedules up to four setTimeouts (loading-screen-delay, already-authenticated-redirect, not-in-telegram-redirect, post-login redirect) inside a useEffect whose deps include isAuthenticated / authLoading — both of which change during loginWithTelegram, re-running the effect mid-flight. None of the timers were tracked, so the previous run's pending navigate() callbacks fired after the new run started, double-triggering setState on the new closure. Route every schedule through a local timers[] that the effect cleanup flushes. Same UX on the happy path; no stray late navigations on the re-entrancy edge.
This commit is contained in:
@@ -68,11 +68,20 @@ export default function TelegramRedirect() {
|
|||||||
const redirectTo = getSafeRedirectUrl(searchParams.get('redirect'));
|
const redirectTo = getSafeRedirectUrl(searchParams.get('redirect'));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// All timers scheduled inside this effect funnel through `timers` so the
|
||||||
|
// cleanup can cancel everything when the effect re-runs (deps change
|
||||||
|
// during loginWithTelegram) or the page unmounts — preventing
|
||||||
|
// setState-on-unmounted-component warnings and stray late navigations.
|
||||||
|
const timers: ReturnType<typeof setTimeout>[] = [];
|
||||||
|
const schedule = (fn: () => void, ms: number) => {
|
||||||
|
timers.push(setTimeout(fn, ms));
|
||||||
|
};
|
||||||
|
|
||||||
// If already authenticated, redirect immediately
|
// If already authenticated, redirect immediately
|
||||||
if (isAuthenticated && !authLoading) {
|
if (isAuthenticated && !authLoading) {
|
||||||
setStatus('success');
|
setStatus('success');
|
||||||
setTimeout(() => navigate(redirectTo), 500);
|
schedule(() => navigate(redirectTo), 500);
|
||||||
return;
|
return () => timers.forEach(clearTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const initTelegram = async () => {
|
const initTelegram = async () => {
|
||||||
@@ -82,7 +91,7 @@ export default function TelegramRedirect() {
|
|||||||
if (!isInTelegramWebApp() || !initData) {
|
if (!isInTelegramWebApp() || !initData) {
|
||||||
// Not in Telegram, show message and redirect to login
|
// Not in Telegram, show message and redirect to login
|
||||||
setStatus('not-telegram');
|
setStatus('not-telegram');
|
||||||
setTimeout(() => navigate('/login'), 2000);
|
schedule(() => navigate('/login'), 2000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,11 +100,8 @@ export default function TelegramRedirect() {
|
|||||||
try {
|
try {
|
||||||
await loginWithTelegram(initData);
|
await loginWithTelegram(initData);
|
||||||
setStatus('success');
|
setStatus('success');
|
||||||
|
|
||||||
// Small delay for nice UX
|
// Small delay for nice UX
|
||||||
setTimeout(() => {
|
schedule(() => navigate(redirectTo), 800);
|
||||||
navigate(redirectTo);
|
|
||||||
}, 800);
|
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.error('Telegram auth failed:', err);
|
console.error('Telegram auth failed:', err);
|
||||||
const error = err as { response?: { data?: { detail?: string } } };
|
const error = err as { response?: { data?: { detail?: string } } };
|
||||||
@@ -105,7 +111,9 @@ export default function TelegramRedirect() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Small delay to show loading screen
|
// Small delay to show loading screen
|
||||||
setTimeout(initTelegram, 300);
|
schedule(initTelegram, 300);
|
||||||
|
|
||||||
|
return () => timers.forEach(clearTimeout);
|
||||||
}, [loginWithTelegram, navigate, isAuthenticated, authLoading, redirectTo, t]);
|
}, [loginWithTelegram, navigate, isAuthenticated, authLoading, redirectTo, t]);
|
||||||
|
|
||||||
// Handle retry with limit to prevent infinite loops
|
// Handle retry with limit to prevent infinite loops
|
||||||
|
|||||||
Reference in New Issue
Block a user