A React Native library for detecting user activity and handling app inactivity with native module support for iOS and Android. Perfect for implementing auto-lock functionality, session management, and security features.
- 🚀 Native Performance: Uses native modules for efficient activity detection
- 📱 Cross-Platform: Supports both iOS and Android
- ⚡ TypeScript Support: Fully typed with TypeScript
- 🎯 Customizable: Configurable timeouts and callbacks
- 🔒 Security Focused: Ideal for apps requiring auto-lock functionality
- 📊 Background Detection: Handles app background/foreground transitions
- 🎮 Touch & Scroll Detection: Detects various user interactions
- 🔄 Optimized Performance: Built-in render optimization to prevent excessive re-renders
npm install react-native-user-activity-detection- Run pod install:
cd ios && pod install- Run ./gradlew build:
cd android && ./gradlew buildimport React, { useCallback, useMemo } from 'react';
import { View, Text, Alert } from 'react-native';
import { useNativeActivityDetection } from 'react-native-user-activity-detection';
const App = () => {
// Memoize callbacks to prevent unnecessary re-renders
const handleInactivity = useCallback(() => {
Alert.alert('Session Expired', 'Please login again');
// Lock the app or navigate to login screen
}, []);
const handleActivity = useCallback(() => {
console.log('User is active');
}, []);
const handleBackground = useCallback(() => {
console.log('App went to background');
}, []);
const handleForeground = useCallback(() => {
console.log('App came to foreground');
}, []);
// Memoize options object to prevent hook re-initialization
const activityOptions = useMemo(() => ({
inactivityTimeout: 300000, // 5 minutes
backgroundTimeout: 120, // 2 minutes
onInactivity: handleInactivity,
onActivity: handleActivity,
onBackground: handleBackground,
onForeground: handleForeground,
debug: false
}), [handleInactivity, handleActivity, handleBackground, handleForeground]);
const { isModuleAvailable, resetTimer } = useNativeActivityDetection(activityOptions);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Native Module Available: {isModuleAvailable ? 'Yes' : 'No'}</Text>
<Text>Activity detection is running...</Text>
</View>
);
};
export default App;For best performance in complex applications, follow these patterns:
import React, { useState, useCallback, useMemo } from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { useNativeActivityDetection } from 'react-native-user-activity-detection';
// Memoize components that don't need frequent updates
const StatusDisplay = React.memo<{
isModuleAvailable: boolean;
isEnabled: boolean;
activityCount: number;
}>(({ isModuleAvailable, isEnabled, activityCount }) => (
<View>
<Text>Module: {isModuleAvailable ? '✅' : '❌'}</Text>
<Text>Enabled: {isEnabled ? '✅' : '❌'}</Text>
<Text>Activity Count: {activityCount}</Text>
</View>
));
const OptimizedApp = () => {
const [isEnabled, setIsEnabled] = useState(true);
const [activityCount, setActivityCount] = useState(0);
// Stable callback references
const handleInactivity = useCallback(() => {
Alert.alert('Inactivity Detected', 'You have been inactive!');
}, []);
const handleActivity = useCallback(() => {
setActivityCount(prev => prev + 1);
}, []);
const toggleEnabled = useCallback(() => {
setIsEnabled(prev => !prev);
}, []);
// Memoized options prevent hook re-initialization
const options = useMemo(() => ({
enabled: isEnabled,
inactivityTimeout: 10000,
backgroundTimeout: 5000,
onInactivity: handleInactivity,
onActivity: handleActivity,
debug: __DEV__
}), [isEnabled, handleInactivity, handleActivity]);
const { isModuleAvailable, resetTimer } = useNativeActivityDetection(options);
return (
<View style={{ flex: 1, padding: 20 }}>
<StatusDisplay
isModuleAvailable={isModuleAvailable}
isEnabled={isEnabled}
activityCount={activityCount}
/>
<TouchableOpacity onPress={toggleEnabled}>
<Text>{isEnabled ? 'Disable' : 'Enable'} Detection</Text>
</TouchableOpacity>
<TouchableOpacity onPress={resetTimer}>
<Text>Reset Timer</Text>
</TouchableOpacity>
</View>
);
};
export default OptimizedApp;import React, { useCallback, useMemo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useNativeActivityDetection } from 'react-native-user-activity-detection';
import { lockApp, unlockApp, updateActivity } from './store/authSlice';
const AppWithRedux = () => {
const { isAuthenticated, isUnlocked } = useSelector((state) => state.auth);
const dispatch = useDispatch();
// Memoize dispatch callbacks
const handleInactivity = useCallback(() => {
dispatch(lockApp());
}, [dispatch]);
const handleActivity = useCallback(() => {
dispatch(updateActivity());
}, [dispatch]);
// Memoize options to prevent unnecessary hook re-initialization
const options = useMemo(
() => ({
enabled: isAuthenticated && isUnlocked,
inactivityTimeout: 480000, // 8 minutes
backgroundTimeout: 240, // 4 minutes
onInactivity: handleInactivity,
onActivity: handleActivity,
debug: false,
}),
[isAuthenticated, isUnlocked, handleInactivity, handleActivity]
);
useNativeActivityDetection(options);
// Rest of your component...
};| Parameter | Type | Default | Description |
|---|---|---|---|
inactivityTimeout |
number |
480000 |
Inactivity timeout in milliseconds |
backgroundTimeout |
number |
240 |
Background timeout in seconds |
enabled |
boolean |
true |
Whether to enable activity detection |
onInactivity |
() => void |
undefined |
Callback when user becomes inactive |
onActivity |
() => void |
undefined |
Callback when user activity is detected |
onBackground |
() => void |
undefined |
Callback when app goes to background |
onForeground |
() => void |
undefined |
Callback when app comes to foreground |
debug |
boolean |
false |
Enable debug logging |
| Property | Type | Description |
|---|---|---|
resetTimer |
() => void |
Reset the inactivity timer |
isModuleAvailable |
boolean |
Whether the native module is available |
Always wrap your callback functions with useCallback to prevent unnecessary re-renders:
const handleInactivity = useCallback(() => {
// Your logic here
}, []);Wrap the options object in useMemo to prevent hook re-initialization:
const options = useMemo(
() => ({
inactivityTimeout: 300000,
onInactivity: handleInactivity,
// ... other options
}),
[handleInactivity]
);Memoize components that don't need frequent updates:
const StatusComponent = React.memo(({ status }) => (
<Text>{status}</Text>
));Don't pass inline functions as callbacks:
// ❌ Bad - creates new function on every render
useNativeActivityDetection({
onInactivity: () => console.log('inactive'),
});
// ✅ Good - stable reference
const handleInactivity = useCallback(() => {
console.log('inactive');
}, []);
useNativeActivityDetection({
onInactivity: handleInactivity,
});The library uses native modules to detect user interactions at the platform level:
- iOS: Intercepts touch events and system notifications
- Android: Uses touch listeners and activity lifecycle callbacks
- Touch Events: Detects taps, swipes, and other touch interactions
- Scroll Events: Monitors scrolling activity in views
- App State Changes: Tracks when the app goes to background/foreground
- System Events: Listens to relevant system-level activity indicators
- Maintains an inactivity timer that resets on user activity
- Separate handling for background time tracking
- Automatic cleanup of timers and listeners
- Optimized to prevent memory leaks and excessive re-renders
- Stable References: All internal callbacks maintain stable references
- Single Initialization: Hook initializes only once, preventing redundant setups
- Efficient Cleanup: Proper cleanup of all subscriptions and timers
- Memoized Returns: Return values are memoized to prevent unnecessary re-renders
If isModuleAvailable returns false:
- iOS: Ensure you've run
pod install - Android: Ensure you've run
./gradlew build - Metro: Try clearing Metro cache:
npx react-native start --reset-cache - Clean Build: Try cleaning and rebuilding your project
- Ensure the hook is enabled (
enabled: true) - Check that your app has proper touch targets
- Verify native module setup
- Enable debug mode to see activity logs
- Ensure callbacks are wrapped with
useCallback - Memoize the options object with
useMemo - Use
React.memofor components that don't need frequent updates - Avoid passing inline functions as callbacks
- iOS: Check app background execution permissions
- Android: Verify activity lifecycle handling
- Test background timeout values are appropriate for your use case
The hook now requires memoized callbacks for optimal performance:
// v1.x - may cause performance issues
useNativeActivityDetection({
onInactivity: () => console.log('inactive'),
});
// v2.x - optimized approach
const handleInactivity = useCallback(() => {
console.log('inactive');
}, []);
const options = useMemo(
() => ({
onInactivity: handleInactivity,
}),
[handleInactivity]
);
useNativeActivityDetection(options);See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made by ❤️ with create-react-native-library
👤 Hamza Gulraiz