react-native-leak-detector
v1.1.0
Published
Static AST analysis tool for React Native memory leak patterns
Maintainers
Readme
react-native-leak-detector 🕵️♂️
A zero-configuration, static AST analysis tool designed specifically for React and React Native projects. It detects common memory leak patterns that standard linters (like ESLint) often miss.
Why this exists
Memory leaks in React Native can silently crash your app with Out-of-Memory (OOM) exceptions. While ESLint react-hooks/exhaustive-deps ensures variables are synchronized, it doesn't verify if your timers, background subscriptions, and arrays are safely bounded and cleaned up.
react-native-leak-detector scans your source code using Babel AST traversal to identify high-risk logical leaks before they hit production. It is written in TypeScript and automatically respects your .gitignore rules!
🚀 Installation & Usage
You don't even need to install it! You can run it instantly using npx:
# Scan your entire source directory
npx react-native-leak-detector ./src
# Scan a single file
npx react-native-leak-detector ./src/components/MyComponent.tsx
# Output results as JSON (useful for custom scripts)
npx react-native-leak-detector ./src --format json
# Output results as a Markdown file (useful for GitHub Action PR comments)
npx react-native-leak-detector ./src --format markdownNote: The detector automatically reads your
.gitignoreand.eslintignorefiles in the target directory and skips scanning those folders (likeios/,android/, andnode_modules/).
Install as a Dev Dependency
If you want to add it to your CI/CD pipeline or package.json scripts:
npm install --save-dev react-native-leak-detector
# or
yarn add -D react-native-leak-detectorThen add a script to your package.json:
"scripts": {
"lint:leaks": "react-native-leak-detector ./src"
}You can then run the detector via:
npm run lint:leaks
# or
yarn lint:leaksGitHub Actions Integration
We provide a drop-in template for GitHub Actions! Simply copy the contents of examples/github-action.yml into your project's .github/workflows/leak-detector.yml to automatically scan for memory leaks on every pull request.
🔍 What it Catches
1. Unbounded Array Growth (HIGH Severity)
Flags setState calls that spread arrays without capping their length. This is a massive cause of OOM crashes when hooked up to fast-firing intervals, frame processors, or location updates.
❌ Bad:
// If called repeatedly in the background, memory will grow infinitely
setLocations(prev => [...prev, newLocation]);✅ Good:
// Safely caps the history at 100 items
setLocations(prev => [...prev, newLocation].slice(-100));2. Missing Effect Cleanups (HIGH Severity)
Identifies useEffect hooks that instantiate timers, intervals, or event listeners, but fail to return a teardown function.
❌ Bad:
useEffect(() => {
const timer = setTimeout(() => doSomething(), 5000);
// Leak: Timer keeps running if component unmounts early!
}, []);3. Suspicious Effect Cleanups (MEDIUM Severity)
Identifies useEffect hooks that return a cleanup function, but the cleanup function doesn't appear to actually call a standard teardown method (clear, remove, stop, unsubscribe).
4. Unbalanced Timers (MEDIUM Severity)
Detects setTimeout or setInterval calls that aren't matched with a corresponding clearTimeout or clearInterval within the same file (unless returned to the caller).
5. Camera Lifecycles (HIGH Severity)
Ensures native camera sessions (like startRecording) are properly stopped in the same scope to prevent the native camera module from running invisibly in the background.
6. Animated Value Recreation (HIGH Severity)
Flags instances where new Animated.Value(0) or new Animated.ValueXY() are instantiated directly inside a functional component body. This causes a new animation object to be instantiated in memory on every render, leading to memory bloat and broken animations. Instead, they should be wrapped in useRef(...).current.
7. Missing Ref Cleanups (MEDIUM Severity)
Flags instances where a timer or subscription is assigned to a useRef (e.g., timerRef.current = setInterval(...)), but the file does not contain a corresponding cleanup call (e.g., clearInterval(timerRef.current)) in an unmount hook.
8. FlatList Inline Renders (MEDIUM Severity)
Flags instances where the renderItem prop of a FlatList or SectionList is defined as an inline anonymous function. In large lists, this creates a new function reference every render, destroying item memoization and spiking memory usage. It should be extracted via useCallback or moved outside the component.
⚙️ Configuration (Optional)
react-native-leak-detector is smart enough to work out-of-the-box. However, if you use custom hooks or custom cleanup methods, you can configure the detector by creating a detect-leaks.config.js file in your project root:
module.exports = {
// Add custom hooks that behave like useEffect
EFFECT_HOOKS: [
'useEffect',
'useFocusEffect',
'useLayoutEffect',
'useMyCustomEffect'
],
// Add custom cleanup function names that indicate teardown
VALID_CLEANUP_KEYWORDS: [
'clear',
'remove',
'stop',
'unsubscribe',
'dispose',
'cancel',
'teardown'
],
// Custom subscription methods that require cleanup
SUBSCRIBE_METHODS: [
'addListener',
'addEventListener',
'on',
'subscribe',
'watchPosition'
],
// Custom timer methods
TIMER_START: ['setInterval', 'setTimeout', 'requestAnimationFrame'],
TIMER_CLEAR: ['clearInterval', 'clearTimeout', 'cancelAnimationFrame'],
};🛑 Ignoring False Positives
If the detector flags a line that you know is safe, you can silence the warning by adding an inline comment immediately above the offending line:
// leak-detector-disable-next-line
AppState.addEventListener('change', mySafeHandler);🤝 Contributing
Contributions, issues, and feature requests are welcome!
📝 License
This project is MIT licensed.
