Hone logo
Hone
Problems

React Memory Leak Detector

Memory leaks are a common and insidious problem in JavaScript applications, especially in complex UIs like those built with React. Identifying and fixing these leaks is crucial for maintaining application performance and stability. This challenge asks you to build a basic memory leak detector for React components.

Problem Description

Your task is to create a React hook, useMemoryLeakDetector, that helps identify potential memory leaks within a component. This hook should track when a component mounts and unmounts, and report any asynchronous operations (like setTimeout or setInterval) that are still active after the component has unmounted.

Key Requirements:

  1. Mount/Unmount Tracking: The hook must reliably detect when the component it's used in mounts and unmounts.
  2. Async Operation Tracking: It should be able to "intercept" or track common asynchronous operations initiated within the component's lifecycle. For simplicity, focus on setTimeout and setInterval.
  3. Leak Detection: If an asynchronous operation initiated by a component is still pending when that component unmounts, it should be flagged as a potential memory leak.
  4. Reporting: The detector should provide a mechanism to report these potential leaks, perhaps by logging them to the console or returning a status.
  5. Cleanup: The hook must correctly clean up its own internal state and any tracked timers when the component unmounts.

Expected Behavior:

When a component using useMemoryLeakDetector mounts, the hook should start tracking. If setTimeout or setInterval is called within that component (or its children/effects), the hook should register those timers. Upon unmounting, the hook should check if any registered timers are still active. If so, it should report them.

Edge Cases:

  • Nested Components: Consider how the detector should behave with nested components. Should it track leaks for all descendants, or only for the direct component? (For this challenge, focus on leaks initiated directly by the component using the hook).
  • Multiple Timers: A component might schedule multiple timers. The detector should handle this.
  • Timers Cleared Explicitly: If a timer is explicitly cleared (e.g., using clearTimeout or clearInterval), it should not be reported as a leak.
  • Timers within Effects: Many timers are set within useEffect hooks. The detector needs to work correctly in this context.

Examples

Example 1: Simple setTimeout Leak

// Component that leaks
function LeakyComponent() {
  const detector = useMemoryLeakDetector('LeakyComponent'); // Pass a name for identification

  useEffect(() => {
    detector.trackTimeout(() => {
      console.log('This might be a leak!');
    }, 5000); // Schedule a timeout
  }, []);

  return <div>I will leak if unmounted before 5 seconds!</div>;
}

// App rendering and unmounting LeakyComponent after 2 seconds
function App() {
  const [show, setShow] = useState(true);

  useEffect(() => {
    const timer = setTimeout(() => {
      setShow(false);
    }, 2000);
    return () => clearTimeout(timer); // Cleanup for App's own timer
  }, []);

  return (
    <div>
      {show && <LeakyComponent />}
      <p>Content below</p>
    </div>
  );
}

Expected Output (in the console after App unmounts LeakyComponent):

[MemoryLeakDetector] Potential leak detected in 'LeakyComponent': setTimeout with ID [some_id] was still active after component unmounted.

(Note: The exact output might vary depending on the implementation details, but the core message should indicate a leak from 'LeakyComponent' due to an active timeout.)

Explanation:

LeakyComponent schedules a setTimeout for 5 seconds. The App component unmounts LeakyComponent after 2 seconds. Since the timeout is still pending at the time of unmount, the useMemoryLeakDetector hook within LeakyComponent should detect and report this as a potential leak.

Example 2: No Leak (Timer Cleared)

// Component that correctly cleans up
function CleanComponent() {
  const detector = useMemoryLeakDetector('CleanComponent');

  useEffect(() => {
    const timerId = detector.trackTimeout(() => {
      console.log('This should not be logged if cleaned up!');
    }, 5000);

    // Explicitly clear the timeout when the component unmounts
    return () => {
      detector.clearTimeout(timerId);
    };
  }, []);

  return <div>I clean up after myself.</div>;
}

Expected Output:

No memory leak related messages in the console from CleanComponent.

Explanation:

CleanComponent uses detector.trackTimeout to get a timer ID. It then uses the returned cleanup function from useEffect to call detector.clearTimeout with that ID. This ensures the timer is cancelled before the component unmounts, preventing a leak.

Constraints

  • The hook should be implemented in TypeScript.
  • The detector should focus on tracking setTimeout and setInterval.
  • The reporting mechanism should be a simple console log.
  • Performance is a consideration; the overhead of the detector should be minimal for typical use cases.
  • The hook should not interfere with React's normal rendering or lifecycle.

Notes

  • You'll likely need to use useEffect to manage the lifecycle of your hook's tracking mechanism.
  • Consider how you will store the active timers. A Map or Set could be useful.
  • The setTimeout and setInterval functions return unique IDs that you can use to clear them. Your hook will need to capture and manage these IDs.
  • Think about how to make the detector reusable and identifiable (e.g., by passing a component name to the hook).
  • While this is a simplified detector, real-world memory leak detection in React often involves more sophisticated techniques and browser developer tools. This challenge aims to build a foundational understanding.
Loading editor...
typescript