📏 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
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:
- useEffect with async function but no AbortController
- Fetch/axios call without signal option
- State updates without mounted check
- setTimeout/setInterval without cleanup
Reviews (0)
Sign in to write a review.
No reviews yet. Be the first to review!
Comments (0)
No comments yet. Be the first to share your thoughts!