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
\`\`\`typescript
// ✅ 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
\`\`\`typescript
// ✅ 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
\`\`\`typescript
// ✅ 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
\`\`\`typescript
// ❌ 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 cleanupRelated Rules
The Ultimate Frontend Development Instruction
cursor.directory
TypeScriptReactNext.js
Next.js React TypeScript Cursor Rules
cursor.directory
Next.jsReactTypeScript
Next.js React TypeScript Cursor Rules
cursor.directory
ReactViteViem v2
OnchainKit Cursor Rules
cursor.directory
ReactOnchainKitTypescript
Plasmic Cursor Rules
cursor.directory
PlasmicLow-codeReact
Enterprise React 19 + Vite 7 Architecture
cursor.directory
ReactViteTypeScript