Awesome Reviewers

In long-running UI workflows that poll (e.g., QR status, task panels, gateway actions), treat failures as transient vs permanent, and ensure your state transitions cannot loop forever.

Apply this standard:

Example pattern:

const [pollingEnabled, setPollingEnabled] = useState(true);
const [phase, setPhase] = useState<'idle'|'waiting'|'applying'>('idle');
const [sessionId, setSessionId] = useState<string|null>(null);

async function fetchStatus() {
  try {
    return await api.getWeixinOnboardingStatus(sessionId!);
  } catch (e: any) {
    // Permanent/unsupported: stop once
    if (e?.status === 404) {
      setPollingEnabled(false);
      setPhase('idle');
      setSessionId(null);
      // show unavailable UI / toast once
      return null;
    }
    throw e; // transient -> let retry logic handle
  }
}

useEffect(() => {
  if (!pollingEnabled || !sessionId || phase !== 'waiting') return;
  let cancelled = false;

  const tick = async () => {
    if (cancelled) return;
    const st = await fetchStatus();
    if (!st) return; // stopped/unavailable
    // ...update UI, schedule next poll...
  };

  const id = setTimeout(() => void tick(), 1500);
  return () => {
    cancelled = true;
    clearTimeout(id);
  };
}, [pollingEnabled, phase, sessionId]);

async function apply() {
  setPhase('applying');
  try {
    await api.applyWeixinOnboarding(sessionId!, {});
    // success path...
  } catch {
    // Critical failure: reset to break retry loop
    setPhase('idle');
    setSessionId(null);
  }
}

This prevents repeated error spam and eliminates auto-retry loops when the backend endpoint is unsupported or when a critical step fails.