📏 Rules

Async Patterns Guard

You are an expert in preventing race conditions and memory leaks in React async operations. ## The Golden Rule EVERY async operation in useEffect MUST have an AbortController. ## Pattern: Async Dat

❤️ 0
⬇️ 0
👁 2
Share

Description

You are an expert in preventing race conditions and memory leaks in React async operations.

The Golden Rule

EVERY async operation in useEffect MUST have an AbortController.

Pattern: Async Data Fetching

// ✅ ALWAYS use this pattern
useEffect(() => {
  const controller = new AbortController();
  let isMounted = true;

  const fetchData = async () => {
    try {
      setLoading(true);
      const response = await fetch(url, {
        signal: controller.signal
      });
      
      if (!response.ok) throw new Error('Failed');
      
      const data = await response.json();
      
      // Only update state if still mounted
      if (isMounted) {
        setData(data);
        setError(null);
      }
    } catch (err) {
      // Ignore abort errors
      if (err.name !== 'AbortError' && isMounted) {
        setError(err);
      }
    } finally {
      if (isMounted) {
        setLoading(false);
      }
    }
  };

  fetchData();

  return () => {
    isMounted = false;
    controller.abort();
  };
}, [url]);

Pattern: Debounced Search

// ✅ Debounce with cleanup
useEffect(() => {
  const controller = new AbortController();
  
  const timeoutId = setTimeout(async () => {
    try {
      const results = await searchApi(query, {
        signal: controller.signal
      });
      setResults(results);
    } catch (err) {
      if (err.name !== 'AbortError') {
        setError(err);
      }
    }
  }, 300);

  return () => {
    clearTimeout(timeoutId);
    controller.abort();
  };
}, [query]);

Pattern: Polling with Cleanup

// ✅ Interval with abort
useEffect(() => {
  const controller = new AbortController();
  
  const poll = async () => {
    try {
      const status = await checkStatus({
        signal: controller.signal
      });
      setStatus(status);
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.error('Polling error:', err);
      }
    }
  };

  poll(); // Initial fetch
  const intervalId = setInterval(poll, 5000);

  return () => {
    clearInterval(intervalId);
    controller.abort();
  };
}, []);

Anti-Patterns to Avoid

// ❌ No cleanup - causes memory leaks
useEffect(() => {
  fetchData().then(setData);
}, [id]);

// ❌ No abort - causes race conditions
useEffect(() => {
  const load = async () => {
    const data = await fetchData();
    setData(data); // May update with stale data
  };
  load();
}, [id]);

// ❌ Ignore pattern without abort check
useEffect(() => {
  let ignore = false;
  fetchData().then(data => {
    if (!ignore) setData(data);
  });
  return () => { ignore = true; };
}, [id]);
// Still has in-flight request!

Detection Rules

Flag as violation:

  1. useEffect with async function but no AbortController
  2. Fetch/axios call without signal option
  3. State updates without mounted check
  4. setTimeout/setInterval without cleanup

Reviews (0)

Sign in to write a review.

No reviews yet. Be the first to review!

Comments (0)

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts!

Compatible Platforms

Pricing

Free

Related Configs