Debuggler LogoDebuggler
All challenges
The Infinite Data LoopAPI / SyncSWR
import { useState, useEffect, useRef } from "react";

// Mock API fetch
async function fetchSystemLogs(options) {
  await new Promise(r => setTimeout(r, 100));
  return [
    { id: 1, type: "info", message: "Database connection established." },
    { id: 2, type: "warning", message: "CPU utilization exceeded 85%." },
    { id: 3, type: "info", message: "SSL certificate renewed successfully." }
  ];
}

export default function App() {
  const [logs, setLogs] = useState([]);
  const [requestCount, setRequestCount] = useState(0);
  const [errorBanner, setErrorBanner] = useState("");
  
  const countRef = useRef(0);

  useEffect(() => {
    const b = document.body;
    b.style.background = "#fafafa"; b.style.margin = "0";
    return () => { b.style.background = ""; b.style.margin = ""; };
  }, []);

  // BUG: Passing an inline object literal directly as a dependency in useEffect.
  // Because {} !== {} in JavaScript, the 'options' object is a new reference
  // on every single render. This triggers the effect, which fetches data and
  // updates state, causing another render, which creates a new 'options' object,
  // triggering the effect again, leading to an infinite fetch loop!
  const options = { fetchPolicy: "network-only" };

  useEffect(() => {
    // Safeguard to prevent crashing the Sandpack browser tab
    if (countRef.current > 40) {
      setErrorBanner("⚠️ CRITICAL: Infinite request loop detected! Over 40 API requests triggered.");
      return;
    }
    
    countRef.current += 1;
    setRequestCount(countRef.current);

    fetchSystemLogs(options).then((data) => {
      setLogs(data);
    });
  }, [options]); // Bug lives here

  return (
    <div style={styles.page}>
      <div style={styles.card}>
        <div style={styles.header}>
          <h1 style={styles.h1}>Activity Monitor</h1>
          <div data-testid="request-badge" style={requestCount > 10 ? styles.badgeAlert : styles.badge}>
            Requests: {requestCount}
          </div>
        </div>

        {errorBanner && (
          <div data-testid="error-banner" style={styles.errorBanner}>
            {errorBanner}
          </div>
        )}

        <p style={styles.sub}>
          Real-time feed of system events and alerts.
        </p>

        <div style={styles.logsList}>
          {logs.map((log) => (
            <div key={log.id} style={styles.logRow}>
              <span style={log.type === "warning" ? styles.typeWarning : styles.typeInfo}>
                [{log.type.toUpperCase()}]
              </span>
              <span style={styles.logMessage}>{log.message}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

const styles = {
  page: { display: "flex", justifyContent: "center", padding: "40px 20px", fontFamily: "system-ui, sans-serif" },
  card: { background: "#fff", borderRadius: 12, padding: "24px", width: "100%", maxWidth: 460, boxShadow: "0 4px 6px -1px rgba(0,0,0,0.1)" },
  header: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 },
  h1: { margin: 0, fontSize: 20, fontWeight: 600, color: "#111827" },
  sub: { margin: "0 0 20px", fontSize: 14, color: "#6B7280" },
  badge: { background: "#F3F4F6", color: "#374151", fontSize: 12, fontWeight: 600, padding: "4px 10px", borderRadius: 9999 },
  badgeAlert: { background: "#FEE2E2", color: "#EF4444", fontSize: 12, fontWeight: 600, padding: "4px 10px", borderRadius: 9999 },
  errorBanner: { background: "#FEF2F2", border: "1px solid #FCA5A5", borderRadius: 8, padding: "12px", color: "#B91C1C", fontSize: 13, fontWeight: 500, marginBottom: 16 },
  logsList: { border: "1px solid #E5E7EB", borderRadius: 8, overflow: "hidden", background: "#FAFBFB" },
  logRow: { display: "flex", gap: 8, padding: "12px 16px", borderBottom: "1px solid #E5E7EB", fontSize: 13, color: "#374151" },
  typeInfo: { color: "#3B82F6", fontWeight: 600 },
  typeWarning: { color: "#F59E0B", fontWeight: 600 },
  logMessage: { color: "#1F2937" }
};

Inspect the Activity Monitor. Although it displays the logs, it executes an infinite number of API requests, taxing the server and slowing down the UI. Stabilize the request system.

After you fix it

  • The component should make only 1 (or 2 in strict mode) initial request on mount.
  • The request count should not continuously tick upwards.

Click "Run checks" to verify your solution.